18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> 88c2ecf20Sopenharmony_ci * Peter Ujfalusi <peter.ujfalusi@ti.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <sound/core.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 208c2ecf20Sopenharmony_ci#include <sound/initval.h> 218c2ecf20Sopenharmony_ci#include <sound/soc.h> 228c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "omap-mcbsp-priv.h" 258c2ecf20Sopenharmony_ci#include "omap-mcbsp.h" 268c2ecf20Sopenharmony_ci#include "sdma-pcm.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cienum { 318c2ecf20Sopenharmony_ci OMAP_MCBSP_WORD_8 = 0, 328c2ecf20Sopenharmony_ci OMAP_MCBSP_WORD_12, 338c2ecf20Sopenharmony_ci OMAP_MCBSP_WORD_16, 348c2ecf20Sopenharmony_ci OMAP_MCBSP_WORD_20, 358c2ecf20Sopenharmony_ci OMAP_MCBSP_WORD_24, 368c2ecf20Sopenharmony_ci OMAP_MCBSP_WORD_32, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); 428c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2)); 438c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1)); 448c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2)); 458c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1)); 468c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2)); 478c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1)); 488c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2)); 498c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1)); 508c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2)); 518c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1)); 528c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2)); 538c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1)); 548c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0)); 558c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "***********************\n"); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct clk *fck_src; 618c2ecf20Sopenharmony_ci const char *src; 628c2ecf20Sopenharmony_ci int r; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (fck_src_id == MCBSP_CLKS_PAD_SRC) 658c2ecf20Sopenharmony_ci src = "pad_fck"; 668c2ecf20Sopenharmony_ci else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) 678c2ecf20Sopenharmony_ci src = "prcm_fck"; 688c2ecf20Sopenharmony_ci else 698c2ecf20Sopenharmony_ci return -EINVAL; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci fck_src = clk_get(mcbsp->dev, src); 728c2ecf20Sopenharmony_ci if (IS_ERR(fck_src)) { 738c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); 748c2ecf20Sopenharmony_ci return -EINVAL; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (mcbsp->active) 788c2ecf20Sopenharmony_ci pm_runtime_put_sync(mcbsp->dev); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci r = clk_set_parent(mcbsp->fclk, fck_src); 818c2ecf20Sopenharmony_ci if (r) 828c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", 838c2ecf20Sopenharmony_ci src); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (mcbsp->active) 868c2ecf20Sopenharmony_ci pm_runtime_get_sync(mcbsp->dev); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci clk_put(fck_src); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return r; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = data; 968c2ecf20Sopenharmony_ci u16 irqst; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci irqst = MCBSP_READ(mcbsp, IRQST); 998c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (irqst & RSYNCERREN) 1028c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); 1038c2ecf20Sopenharmony_ci if (irqst & RFSREN) 1048c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "RX Frame Sync\n"); 1058c2ecf20Sopenharmony_ci if (irqst & REOFEN) 1068c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "RX End Of Frame\n"); 1078c2ecf20Sopenharmony_ci if (irqst & RRDYEN) 1088c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); 1098c2ecf20Sopenharmony_ci if (irqst & RUNDFLEN) 1108c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); 1118c2ecf20Sopenharmony_ci if (irqst & ROVFLEN) 1128c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (irqst & XSYNCERREN) 1158c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); 1168c2ecf20Sopenharmony_ci if (irqst & XFSXEN) 1178c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "TX Frame Sync\n"); 1188c2ecf20Sopenharmony_ci if (irqst & XEOFEN) 1198c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "TX End Of Frame\n"); 1208c2ecf20Sopenharmony_ci if (irqst & XRDYEN) 1218c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); 1228c2ecf20Sopenharmony_ci if (irqst & XUNDFLEN) 1238c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); 1248c2ecf20Sopenharmony_ci if (irqst & XOVFLEN) 1258c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); 1268c2ecf20Sopenharmony_ci if (irqst & XEMPTYEOFEN) 1278c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, IRQST, irqst); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = data; 1378c2ecf20Sopenharmony_ci u16 irqst_spcr2; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); 1408c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (irqst_spcr2 & XSYNC_ERR) { 1438c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", 1448c2ecf20Sopenharmony_ci irqst_spcr2); 1458c2ecf20Sopenharmony_ci /* Writing zero to XSYNC_ERR clears the IRQ */ 1468c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = data; 1558c2ecf20Sopenharmony_ci u16 irqst_spcr1; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); 1588c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (irqst_spcr1 & RSYNC_ERR) { 1618c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", 1628c2ecf20Sopenharmony_ci irqst_spcr1); 1638c2ecf20Sopenharmony_ci /* Writing zero to RSYNC_ERR clears the IRQ */ 1648c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * omap_mcbsp_config simply write a config to the 1728c2ecf20Sopenharmony_ci * appropriate McBSP. 1738c2ecf20Sopenharmony_ci * You either call this function or set the McBSP registers 1748c2ecf20Sopenharmony_ci * by yourself before calling omap_mcbsp_start(). 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic void omap_mcbsp_config(struct omap_mcbsp *mcbsp, 1778c2ecf20Sopenharmony_ci const struct omap_mcbsp_reg_cfg *config) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", 1808c2ecf20Sopenharmony_ci mcbsp->id, mcbsp->phys_base); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* We write the given config */ 1838c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); 1848c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); 1858c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, RCR2, config->rcr2); 1868c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, RCR1, config->rcr1); 1878c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, XCR2, config->xcr2); 1888c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, XCR1, config->xcr1); 1898c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); 1908c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); 1918c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, MCR2, config->mcr2); 1928c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, MCR1, config->mcr1); 1938c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, PCR0, config->pcr0); 1948c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_ccr) { 1958c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, XCCR, config->xccr); 1968c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, RCCR, config->rccr); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci /* Enable wakeup behavior */ 1998c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_wakeup) 2008c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Enable TX/RX sync error interrupts by default */ 2038c2ecf20Sopenharmony_ci if (mcbsp->irq) 2048c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | 2058c2ecf20Sopenharmony_ci RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/** 2098c2ecf20Sopenharmony_ci * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register 2108c2ecf20Sopenharmony_ci * @mcbsp: omap_mcbsp struct for the McBSP instance 2118c2ecf20Sopenharmony_ci * @stream: Stream direction (playback/capture) 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * Returns the address of mcbsp data transmit register or data receive register 2148c2ecf20Sopenharmony_ci * to be used by DMA for transferring/receiving data 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, 2178c2ecf20Sopenharmony_ci unsigned int stream) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int data_reg; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 2228c2ecf20Sopenharmony_ci if (mcbsp->pdata->reg_size == 2) 2238c2ecf20Sopenharmony_ci data_reg = OMAP_MCBSP_REG_DXR1; 2248c2ecf20Sopenharmony_ci else 2258c2ecf20Sopenharmony_ci data_reg = OMAP_MCBSP_REG_DXR; 2268c2ecf20Sopenharmony_ci } else { 2278c2ecf20Sopenharmony_ci if (mcbsp->pdata->reg_size == 2) 2288c2ecf20Sopenharmony_ci data_reg = OMAP_MCBSP_REG_DRR1; 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci data_reg = OMAP_MCBSP_REG_DRR; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. 2388c2ecf20Sopenharmony_ci * The threshold parameter is 1 based, and it is converted (threshold - 1) 2398c2ecf20Sopenharmony_ci * for the THRSH2 register. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cistatic void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci if (threshold && threshold <= mcbsp->max_tx_thres) 2448c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * omap_mcbsp_set_rx_threshold configures the receive threshold in words. 2498c2ecf20Sopenharmony_ci * The threshold parameter is 1 based, and it is converted (threshold - 1) 2508c2ecf20Sopenharmony_ci * for the THRSH1 register. 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_cistatic void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci if (threshold && threshold <= mcbsp->max_rx_thres) 2558c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_cistatic u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci u16 buffstat; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Returns the number of free locations in the buffer */ 2668c2ecf20Sopenharmony_ci buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Number of slots are different in McBSP ports */ 2698c2ecf20Sopenharmony_ci return mcbsp->pdata->buffer_size - buffstat; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO 2748c2ecf20Sopenharmony_ci * to reach the threshold value (when the DMA will be triggered to read it) 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci u16 buffstat, threshold; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Returns the number of used locations in the buffer */ 2818c2ecf20Sopenharmony_ci buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); 2828c2ecf20Sopenharmony_ci /* RX threshold */ 2838c2ecf20Sopenharmony_ci threshold = MCBSP_READ(mcbsp, THRSH1); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Return the number of location till we reach the threshold limit */ 2868c2ecf20Sopenharmony_ci if (threshold <= buffstat) 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci return threshold - buffstat; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int omap_mcbsp_request(struct omap_mcbsp *mcbsp) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci void *reg_cache; 2958c2ecf20Sopenharmony_ci int err; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); 2988c2ecf20Sopenharmony_ci if (!reg_cache) 2998c2ecf20Sopenharmony_ci return -ENOMEM; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci spin_lock(&mcbsp->lock); 3028c2ecf20Sopenharmony_ci if (!mcbsp->free) { 3038c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id); 3048c2ecf20Sopenharmony_ci err = -EBUSY; 3058c2ecf20Sopenharmony_ci goto err_kfree; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mcbsp->free = false; 3098c2ecf20Sopenharmony_ci mcbsp->reg_cache = reg_cache; 3108c2ecf20Sopenharmony_ci spin_unlock(&mcbsp->lock); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if(mcbsp->pdata->ops && mcbsp->pdata->ops->request) 3138c2ecf20Sopenharmony_ci mcbsp->pdata->ops->request(mcbsp->id - 1); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * Make sure that transmitter, receiver and sample-rate generator are 3178c2ecf20Sopenharmony_ci * not running before activating IRQs. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR1, 0); 3208c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, 0); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (mcbsp->irq) { 3238c2ecf20Sopenharmony_ci err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, 3248c2ecf20Sopenharmony_ci "McBSP", (void *)mcbsp); 3258c2ecf20Sopenharmony_ci if (err != 0) { 3268c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "Unable to request IRQ\n"); 3278c2ecf20Sopenharmony_ci goto err_clk_disable; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } else { 3308c2ecf20Sopenharmony_ci err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, 3318c2ecf20Sopenharmony_ci "McBSP TX", (void *)mcbsp); 3328c2ecf20Sopenharmony_ci if (err != 0) { 3338c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); 3348c2ecf20Sopenharmony_ci goto err_clk_disable; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, 3388c2ecf20Sopenharmony_ci "McBSP RX", (void *)mcbsp); 3398c2ecf20Sopenharmony_ci if (err != 0) { 3408c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); 3418c2ecf20Sopenharmony_ci goto err_free_irq; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_cierr_free_irq: 3478c2ecf20Sopenharmony_ci free_irq(mcbsp->tx_irq, (void *)mcbsp); 3488c2ecf20Sopenharmony_cierr_clk_disable: 3498c2ecf20Sopenharmony_ci if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) 3508c2ecf20Sopenharmony_ci mcbsp->pdata->ops->free(mcbsp->id - 1); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* Disable wakeup behavior */ 3538c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_wakeup) 3548c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, WAKEUPEN, 0); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci spin_lock(&mcbsp->lock); 3578c2ecf20Sopenharmony_ci mcbsp->free = true; 3588c2ecf20Sopenharmony_ci mcbsp->reg_cache = NULL; 3598c2ecf20Sopenharmony_cierr_kfree: 3608c2ecf20Sopenharmony_ci spin_unlock(&mcbsp->lock); 3618c2ecf20Sopenharmony_ci kfree(reg_cache); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return err; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic void omap_mcbsp_free(struct omap_mcbsp *mcbsp) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci void *reg_cache; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) 3718c2ecf20Sopenharmony_ci mcbsp->pdata->ops->free(mcbsp->id - 1); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Disable wakeup behavior */ 3748c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_wakeup) 3758c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, WAKEUPEN, 0); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Disable interrupt requests */ 3788c2ecf20Sopenharmony_ci if (mcbsp->irq) 3798c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, IRQEN, 0); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (mcbsp->irq) { 3828c2ecf20Sopenharmony_ci free_irq(mcbsp->irq, (void *)mcbsp); 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci free_irq(mcbsp->rx_irq, (void *)mcbsp); 3858c2ecf20Sopenharmony_ci free_irq(mcbsp->tx_irq, (void *)mcbsp); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci reg_cache = mcbsp->reg_cache; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * Select CLKS source from internal source unconditionally before 3928c2ecf20Sopenharmony_ci * marking the McBSP port as free. 3938c2ecf20Sopenharmony_ci * If the external clock source via MCBSP_CLKS pin has been selected the 3948c2ecf20Sopenharmony_ci * system will refuse to enter idle if the CLKS pin source is not reset 3958c2ecf20Sopenharmony_ci * back to internal source. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci if (!mcbsp_omap1()) 3988c2ecf20Sopenharmony_ci omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci spin_lock(&mcbsp->lock); 4018c2ecf20Sopenharmony_ci if (mcbsp->free) 4028c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); 4038c2ecf20Sopenharmony_ci else 4048c2ecf20Sopenharmony_ci mcbsp->free = true; 4058c2ecf20Sopenharmony_ci mcbsp->reg_cache = NULL; 4068c2ecf20Sopenharmony_ci spin_unlock(&mcbsp->lock); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci kfree(reg_cache); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* 4128c2ecf20Sopenharmony_ci * Here we start the McBSP, by enabling transmitter, receiver or both. 4138c2ecf20Sopenharmony_ci * If no transmitter or receiver is active prior calling, then sample-rate 4148c2ecf20Sopenharmony_ci * generator and frame sync are started. 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_cistatic void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); 4198c2ecf20Sopenharmony_ci int rx = !tx; 4208c2ecf20Sopenharmony_ci int enable_srg = 0; 4218c2ecf20Sopenharmony_ci u16 w; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (mcbsp->st_data) 4248c2ecf20Sopenharmony_ci omap_mcbsp_st_start(mcbsp); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Only enable SRG, if McBSP is master */ 4278c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, PCR0); 4288c2ecf20Sopenharmony_ci if (w & (FSXM | FSRM | CLKXM | CLKRM)) 4298c2ecf20Sopenharmony_ci enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | 4308c2ecf20Sopenharmony_ci MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (enable_srg) { 4338c2ecf20Sopenharmony_ci /* Start the sample generator */ 4348c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, SPCR2); 4358c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Enable transmitter and receiver */ 4398c2ecf20Sopenharmony_ci tx &= 1; 4408c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, SPCR2); 4418c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, w | tx); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci rx &= 1; 4448c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, SPCR1); 4458c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR1, w | rx); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* 4488c2ecf20Sopenharmony_ci * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec 4498c2ecf20Sopenharmony_ci * REVISIT: 100us may give enough time for two CLKSRG, however 4508c2ecf20Sopenharmony_ci * due to some unknown PM related, clock gating etc. reason it 4518c2ecf20Sopenharmony_ci * is now at 500us. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci udelay(500); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (enable_srg) { 4568c2ecf20Sopenharmony_ci /* Start frame sync */ 4578c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, SPCR2); 4588c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_ccr) { 4628c2ecf20Sopenharmony_ci /* Release the transmitter and receiver */ 4638c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, XCCR); 4648c2ecf20Sopenharmony_ci w &= ~(tx ? XDISABLE : 0); 4658c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, XCCR, w); 4668c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, RCCR); 4678c2ecf20Sopenharmony_ci w &= ~(rx ? RDISABLE : 0); 4688c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, RCCR, w); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Dump McBSP Regs */ 4728c2ecf20Sopenharmony_ci omap_mcbsp_dump_reg(mcbsp); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); 4788c2ecf20Sopenharmony_ci int rx = !tx; 4798c2ecf20Sopenharmony_ci int idle; 4808c2ecf20Sopenharmony_ci u16 w; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* Reset transmitter */ 4838c2ecf20Sopenharmony_ci tx &= 1; 4848c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_ccr) { 4858c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, XCCR); 4868c2ecf20Sopenharmony_ci w |= (tx ? XDISABLE : 0); 4878c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, XCCR, w); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, SPCR2); 4908c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* Reset receiver */ 4938c2ecf20Sopenharmony_ci rx &= 1; 4948c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_ccr) { 4958c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, RCCR); 4968c2ecf20Sopenharmony_ci w |= (rx ? RDISABLE : 0); 4978c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, RCCR, w); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, SPCR1); 5008c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | 5038c2ecf20Sopenharmony_ci MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (idle) { 5068c2ecf20Sopenharmony_ci /* Reset the sample rate generator */ 5078c2ecf20Sopenharmony_ci w = MCBSP_READ_CACHE(mcbsp, SPCR2); 5088c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (mcbsp->st_data) 5128c2ecf20Sopenharmony_ci omap_mcbsp_st_stop(mcbsp); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci#define max_thres(m) (mcbsp->pdata->buffer_size) 5168c2ecf20Sopenharmony_ci#define valid_threshold(m, val) ((val) <= max_thres(m)) 5178c2ecf20Sopenharmony_ci#define THRESHOLD_PROP_BUILDER(prop) \ 5188c2ecf20Sopenharmony_cistatic ssize_t prop##_show(struct device *dev, \ 5198c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) \ 5208c2ecf20Sopenharmony_ci{ \ 5218c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ 5228c2ecf20Sopenharmony_ci \ 5238c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", mcbsp->prop); \ 5248c2ecf20Sopenharmony_ci} \ 5258c2ecf20Sopenharmony_ci \ 5268c2ecf20Sopenharmony_cistatic ssize_t prop##_store(struct device *dev, \ 5278c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 5288c2ecf20Sopenharmony_ci const char *buf, size_t size) \ 5298c2ecf20Sopenharmony_ci{ \ 5308c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ 5318c2ecf20Sopenharmony_ci unsigned long val; \ 5328c2ecf20Sopenharmony_ci int status; \ 5338c2ecf20Sopenharmony_ci \ 5348c2ecf20Sopenharmony_ci status = kstrtoul(buf, 0, &val); \ 5358c2ecf20Sopenharmony_ci if (status) \ 5368c2ecf20Sopenharmony_ci return status; \ 5378c2ecf20Sopenharmony_ci \ 5388c2ecf20Sopenharmony_ci if (!valid_threshold(mcbsp, val)) \ 5398c2ecf20Sopenharmony_ci return -EDOM; \ 5408c2ecf20Sopenharmony_ci \ 5418c2ecf20Sopenharmony_ci mcbsp->prop = val; \ 5428c2ecf20Sopenharmony_ci return size; \ 5438c2ecf20Sopenharmony_ci} \ 5448c2ecf20Sopenharmony_ci \ 5458c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prop, 0644, prop##_show, prop##_store) 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ciTHRESHOLD_PROP_BUILDER(max_tx_thres); 5488c2ecf20Sopenharmony_ciTHRESHOLD_PROP_BUILDER(max_rx_thres); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const char * const dma_op_modes[] = { 5518c2ecf20Sopenharmony_ci "element", "threshold", 5528c2ecf20Sopenharmony_ci}; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic ssize_t dma_op_mode_show(struct device *dev, 5558c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); 5588c2ecf20Sopenharmony_ci int dma_op_mode, i = 0; 5598c2ecf20Sopenharmony_ci ssize_t len = 0; 5608c2ecf20Sopenharmony_ci const char * const *s; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci dma_op_mode = mcbsp->dma_op_mode; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { 5658c2ecf20Sopenharmony_ci if (dma_op_mode == i) 5668c2ecf20Sopenharmony_ci len += sprintf(buf + len, "[%s] ", *s); 5678c2ecf20Sopenharmony_ci else 5688c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%s ", *s); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci len += sprintf(buf + len, "\n"); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return len; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic ssize_t dma_op_mode_store(struct device *dev, 5768c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 5778c2ecf20Sopenharmony_ci size_t size) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); 5808c2ecf20Sopenharmony_ci int i; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci i = sysfs_match_string(dma_op_modes, buf); 5838c2ecf20Sopenharmony_ci if (i < 0) 5848c2ecf20Sopenharmony_ci return i; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci spin_lock_irq(&mcbsp->lock); 5878c2ecf20Sopenharmony_ci if (!mcbsp->free) { 5888c2ecf20Sopenharmony_ci size = -EBUSY; 5898c2ecf20Sopenharmony_ci goto unlock; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci mcbsp->dma_op_mode = i; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ciunlock: 5948c2ecf20Sopenharmony_ci spin_unlock_irq(&mcbsp->lock); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return size; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(dma_op_mode); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic const struct attribute *additional_attrs[] = { 6028c2ecf20Sopenharmony_ci &dev_attr_max_tx_thres.attr, 6038c2ecf20Sopenharmony_ci &dev_attr_max_rx_thres.attr, 6048c2ecf20Sopenharmony_ci &dev_attr_dma_op_mode.attr, 6058c2ecf20Sopenharmony_ci NULL, 6068c2ecf20Sopenharmony_ci}; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic const struct attribute_group additional_attr_group = { 6098c2ecf20Sopenharmony_ci .attrs = (struct attribute **)additional_attrs, 6108c2ecf20Sopenharmony_ci}; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/* 6138c2ecf20Sopenharmony_ci * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. 6148c2ecf20Sopenharmony_ci * 730 has only 2 McBSP, and both of them are MPU peripherals. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_cistatic int omap_mcbsp_init(struct platform_device *pdev) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); 6198c2ecf20Sopenharmony_ci struct resource *res; 6208c2ecf20Sopenharmony_ci int ret = 0; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci spin_lock_init(&mcbsp->lock); 6238c2ecf20Sopenharmony_ci mcbsp->free = true; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); 6268c2ecf20Sopenharmony_ci if (!res) 6278c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); 6308c2ecf20Sopenharmony_ci if (IS_ERR(mcbsp->io_base)) 6318c2ecf20Sopenharmony_ci return PTR_ERR(mcbsp->io_base); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci mcbsp->phys_base = res->start; 6348c2ecf20Sopenharmony_ci mcbsp->reg_cache_size = resource_size(res); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); 6378c2ecf20Sopenharmony_ci if (!res) 6388c2ecf20Sopenharmony_ci mcbsp->phys_dma_base = mcbsp->phys_base; 6398c2ecf20Sopenharmony_ci else 6408c2ecf20Sopenharmony_ci mcbsp->phys_dma_base = res->start; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* 6438c2ecf20Sopenharmony_ci * OMAP1, 2 uses two interrupt lines: TX, RX 6448c2ecf20Sopenharmony_ci * OMAP2430, OMAP3 SoC have combined IRQ line as well. 6458c2ecf20Sopenharmony_ci * OMAP4 and newer SoC only have the combined IRQ line. 6468c2ecf20Sopenharmony_ci * Use the combined IRQ if available since it gives better debugging 6478c2ecf20Sopenharmony_ci * possibilities. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci mcbsp->irq = platform_get_irq_byname(pdev, "common"); 6508c2ecf20Sopenharmony_ci if (mcbsp->irq == -ENXIO) { 6518c2ecf20Sopenharmony_ci mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (mcbsp->tx_irq == -ENXIO) { 6548c2ecf20Sopenharmony_ci mcbsp->irq = platform_get_irq(pdev, 0); 6558c2ecf20Sopenharmony_ci mcbsp->tx_irq = 0; 6568c2ecf20Sopenharmony_ci } else { 6578c2ecf20Sopenharmony_ci mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); 6588c2ecf20Sopenharmony_ci mcbsp->irq = 0; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (!pdev->dev.of_node) { 6638c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); 6648c2ecf20Sopenharmony_ci if (!res) { 6658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid tx DMA channel\n"); 6668c2ecf20Sopenharmony_ci return -ENODEV; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci mcbsp->dma_req[0] = res->start; 6698c2ecf20Sopenharmony_ci mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); 6728c2ecf20Sopenharmony_ci if (!res) { 6738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid rx DMA channel\n"); 6748c2ecf20Sopenharmony_ci return -ENODEV; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci mcbsp->dma_req[1] = res->start; 6778c2ecf20Sopenharmony_ci mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; 6788c2ecf20Sopenharmony_ci } else { 6798c2ecf20Sopenharmony_ci mcbsp->dma_data[0].filter_data = "tx"; 6808c2ecf20Sopenharmony_ci mcbsp->dma_data[1].filter_data = "rx"; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, 6848c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK); 6858c2ecf20Sopenharmony_ci mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, 6868c2ecf20Sopenharmony_ci SNDRV_PCM_STREAM_CAPTURE); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci mcbsp->fclk = devm_clk_get(&pdev->dev, "fck"); 6898c2ecf20Sopenharmony_ci if (IS_ERR(mcbsp->fclk)) { 6908c2ecf20Sopenharmony_ci ret = PTR_ERR(mcbsp->fclk); 6918c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); 6928c2ecf20Sopenharmony_ci return ret; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; 6968c2ecf20Sopenharmony_ci if (mcbsp->pdata->buffer_size) { 6978c2ecf20Sopenharmony_ci /* 6988c2ecf20Sopenharmony_ci * Initially configure the maximum thresholds to a safe value. 6998c2ecf20Sopenharmony_ci * The McBSP FIFO usage with these values should not go under 7008c2ecf20Sopenharmony_ci * 16 locations. 7018c2ecf20Sopenharmony_ci * If the whole FIFO without safety buffer is used, than there 7028c2ecf20Sopenharmony_ci * is a possibility that the DMA will be not able to push the 7038c2ecf20Sopenharmony_ci * new data on time, causing channel shifts in runtime. 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ci mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; 7068c2ecf20Sopenharmony_ci mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci ret = sysfs_create_group(&mcbsp->dev->kobj, 7098c2ecf20Sopenharmony_ci &additional_attr_group); 7108c2ecf20Sopenharmony_ci if (ret) { 7118c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, 7128c2ecf20Sopenharmony_ci "Unable to create additional controls\n"); 7138c2ecf20Sopenharmony_ci return ret; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci ret = omap_mcbsp_st_init(pdev); 7188c2ecf20Sopenharmony_ci if (ret) 7198c2ecf20Sopenharmony_ci goto err_st; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cierr_st: 7248c2ecf20Sopenharmony_ci if (mcbsp->pdata->buffer_size) 7258c2ecf20Sopenharmony_ci sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); 7268c2ecf20Sopenharmony_ci return ret; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/* 7308c2ecf20Sopenharmony_ci * Stream DMA parameters. DMA request line and port address are set runtime 7318c2ecf20Sopenharmony_ci * since they are different between OMAP1 and later OMAPs 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_cistatic void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, 7348c2ecf20Sopenharmony_ci unsigned int packet_size) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 7378c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 7388c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 7398c2ecf20Sopenharmony_ci int words; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* No need to proceed further if McBSP does not have FIFO */ 7428c2ecf20Sopenharmony_ci if (mcbsp->pdata->buffer_size == 0) 7438c2ecf20Sopenharmony_ci return; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* 7468c2ecf20Sopenharmony_ci * Configure McBSP threshold based on either: 7478c2ecf20Sopenharmony_ci * packet_size, when the sDMA is in packet mode, or based on the 7488c2ecf20Sopenharmony_ci * period size in THRESHOLD mode, otherwise use McBSP threshold = 1 7498c2ecf20Sopenharmony_ci * for mono streams. 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci if (packet_size) 7528c2ecf20Sopenharmony_ci words = packet_size; 7538c2ecf20Sopenharmony_ci else 7548c2ecf20Sopenharmony_ci words = 1; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Configure McBSP internal buffer usage */ 7578c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 7588c2ecf20Sopenharmony_ci omap_mcbsp_set_tx_threshold(mcbsp, words); 7598c2ecf20Sopenharmony_ci else 7608c2ecf20Sopenharmony_ci omap_mcbsp_set_rx_threshold(mcbsp, words); 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, 7648c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct snd_interval *buffer_size = hw_param_interval(params, 7678c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE); 7688c2ecf20Sopenharmony_ci struct snd_interval *channels = hw_param_interval(params, 7698c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS); 7708c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = rule->private; 7718c2ecf20Sopenharmony_ci struct snd_interval frames; 7728c2ecf20Sopenharmony_ci int size; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci snd_interval_any(&frames); 7758c2ecf20Sopenharmony_ci size = mcbsp->pdata->buffer_size; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci frames.min = size / channels->min; 7788c2ecf20Sopenharmony_ci frames.integer = 1; 7798c2ecf20Sopenharmony_ci return snd_interval_refine(buffer_size, &frames); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, 7838c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 7868c2ecf20Sopenharmony_ci int err = 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) 7898c2ecf20Sopenharmony_ci err = omap_mcbsp_request(mcbsp); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* 7928c2ecf20Sopenharmony_ci * OMAP3 McBSP FIFO is word structured. 7938c2ecf20Sopenharmony_ci * McBSP2 has 1024 + 256 = 1280 word long buffer, 7948c2ecf20Sopenharmony_ci * McBSP1,3,4,5 has 128 word long buffer 7958c2ecf20Sopenharmony_ci * This means that the size of the FIFO depends on the sample format. 7968c2ecf20Sopenharmony_ci * For example on McBSP3: 7978c2ecf20Sopenharmony_ci * 16bit samples: size is 128 * 2 = 256 bytes 7988c2ecf20Sopenharmony_ci * 32bit samples: size is 128 * 4 = 512 bytes 7998c2ecf20Sopenharmony_ci * It is simpler to place constraint for buffer and period based on 8008c2ecf20Sopenharmony_ci * channels. 8018c2ecf20Sopenharmony_ci * McBSP3 as example again (16 or 32 bit samples): 8028c2ecf20Sopenharmony_ci * 1 channel (mono): size is 128 frames (128 words) 8038c2ecf20Sopenharmony_ci * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) 8048c2ecf20Sopenharmony_ci * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) 8058c2ecf20Sopenharmony_ci */ 8068c2ecf20Sopenharmony_ci if (mcbsp->pdata->buffer_size) { 8078c2ecf20Sopenharmony_ci /* 8088c2ecf20Sopenharmony_ci * Rule for the buffer size. We should not allow 8098c2ecf20Sopenharmony_ci * smaller buffer than the FIFO size to avoid underruns. 8108c2ecf20Sopenharmony_ci * This applies only for the playback stream. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 8138c2ecf20Sopenharmony_ci snd_pcm_hw_rule_add(substream->runtime, 0, 8148c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 8158c2ecf20Sopenharmony_ci omap_mcbsp_hwrule_min_buffersize, 8168c2ecf20Sopenharmony_ci mcbsp, 8178c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci /* Make sure, that the period size is always even */ 8208c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(substream->runtime, 0, 8218c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci return err; 8258c2ecf20Sopenharmony_ci} 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cistatic void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream, 8288c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 8318c2ecf20Sopenharmony_ci int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); 8328c2ecf20Sopenharmony_ci int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; 8338c2ecf20Sopenharmony_ci int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (mcbsp->latency[stream2]) 8368c2ecf20Sopenharmony_ci cpu_latency_qos_update_request(&mcbsp->pm_qos_req, 8378c2ecf20Sopenharmony_ci mcbsp->latency[stream2]); 8388c2ecf20Sopenharmony_ci else if (mcbsp->latency[stream1]) 8398c2ecf20Sopenharmony_ci cpu_latency_qos_remove_request(&mcbsp->pm_qos_req); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci mcbsp->latency[stream1] = 0; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) { 8448c2ecf20Sopenharmony_ci omap_mcbsp_free(mcbsp); 8458c2ecf20Sopenharmony_ci mcbsp->configured = 0; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream, 8508c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 8538c2ecf20Sopenharmony_ci struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req; 8548c2ecf20Sopenharmony_ci int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); 8558c2ecf20Sopenharmony_ci int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; 8568c2ecf20Sopenharmony_ci int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; 8578c2ecf20Sopenharmony_ci int latency = mcbsp->latency[stream2]; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* Prevent omap hardware from hitting off between FIFO fills */ 8608c2ecf20Sopenharmony_ci if (!latency || mcbsp->latency[stream1] < latency) 8618c2ecf20Sopenharmony_ci latency = mcbsp->latency[stream1]; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (cpu_latency_qos_request_active(pm_qos_req)) 8648c2ecf20Sopenharmony_ci cpu_latency_qos_update_request(pm_qos_req, latency); 8658c2ecf20Sopenharmony_ci else if (latency) 8668c2ecf20Sopenharmony_ci cpu_latency_qos_add_request(pm_qos_req, latency); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci return 0; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, 8728c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci switch (cmd) { 8778c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 8788c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 8798c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 8808c2ecf20Sopenharmony_ci mcbsp->active++; 8818c2ecf20Sopenharmony_ci omap_mcbsp_start(mcbsp, substream->stream); 8828c2ecf20Sopenharmony_ci break; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 8858c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 8868c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 8878c2ecf20Sopenharmony_ci omap_mcbsp_stop(mcbsp, substream->stream); 8888c2ecf20Sopenharmony_ci mcbsp->active--; 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci default: 8918c2ecf20Sopenharmony_ci return -EINVAL; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci return 0; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic snd_pcm_sframes_t omap_mcbsp_dai_delay( 8988c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 8998c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 9028c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 9038c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 9048c2ecf20Sopenharmony_ci u16 fifo_use; 9058c2ecf20Sopenharmony_ci snd_pcm_sframes_t delay; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* No need to proceed further if McBSP does not have FIFO */ 9088c2ecf20Sopenharmony_ci if (mcbsp->pdata->buffer_size == 0) 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 9128c2ecf20Sopenharmony_ci fifo_use = omap_mcbsp_get_tx_delay(mcbsp); 9138c2ecf20Sopenharmony_ci else 9148c2ecf20Sopenharmony_ci fifo_use = omap_mcbsp_get_rx_delay(mcbsp); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* 9178c2ecf20Sopenharmony_ci * Divide the used locations with the channel count to get the 9188c2ecf20Sopenharmony_ci * FIFO usage in samples (don't care about partial samples in the 9198c2ecf20Sopenharmony_ci * buffer). 9208c2ecf20Sopenharmony_ci */ 9218c2ecf20Sopenharmony_ci delay = fifo_use / substream->runtime->channels; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci return delay; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, 9278c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 9288c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 9318c2ecf20Sopenharmony_ci struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; 9328c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 9338c2ecf20Sopenharmony_ci int wlen, channels, wpf; 9348c2ecf20Sopenharmony_ci int pkt_size = 0; 9358c2ecf20Sopenharmony_ci unsigned int format, div, framesize, master; 9368c2ecf20Sopenharmony_ci unsigned int buffer_size = mcbsp->pdata->buffer_size; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); 9398c2ecf20Sopenharmony_ci channels = params_channels(params); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci switch (params_format(params)) { 9428c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 9438c2ecf20Sopenharmony_ci wlen = 16; 9448c2ecf20Sopenharmony_ci break; 9458c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 9468c2ecf20Sopenharmony_ci wlen = 32; 9478c2ecf20Sopenharmony_ci break; 9488c2ecf20Sopenharmony_ci default: 9498c2ecf20Sopenharmony_ci return -EINVAL; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci if (buffer_size) { 9528c2ecf20Sopenharmony_ci int latency; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { 9558c2ecf20Sopenharmony_ci int period_words, max_thrsh; 9568c2ecf20Sopenharmony_ci int divider = 0; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci period_words = params_period_bytes(params) / (wlen / 8); 9598c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 9608c2ecf20Sopenharmony_ci max_thrsh = mcbsp->max_tx_thres; 9618c2ecf20Sopenharmony_ci else 9628c2ecf20Sopenharmony_ci max_thrsh = mcbsp->max_rx_thres; 9638c2ecf20Sopenharmony_ci /* 9648c2ecf20Sopenharmony_ci * Use sDMA packet mode if McBSP is in threshold mode: 9658c2ecf20Sopenharmony_ci * If period words less than the FIFO size the packet 9668c2ecf20Sopenharmony_ci * size is set to the number of period words, otherwise 9678c2ecf20Sopenharmony_ci * Look for the biggest threshold value which divides 9688c2ecf20Sopenharmony_ci * the period size evenly. 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci divider = period_words / max_thrsh; 9718c2ecf20Sopenharmony_ci if (period_words % max_thrsh) 9728c2ecf20Sopenharmony_ci divider++; 9738c2ecf20Sopenharmony_ci while (period_words % divider && 9748c2ecf20Sopenharmony_ci divider < period_words) 9758c2ecf20Sopenharmony_ci divider++; 9768c2ecf20Sopenharmony_ci if (divider == period_words) 9778c2ecf20Sopenharmony_ci return -EINVAL; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci pkt_size = period_words / divider; 9808c2ecf20Sopenharmony_ci } else if (channels > 1) { 9818c2ecf20Sopenharmony_ci /* Use packet mode for non mono streams */ 9828c2ecf20Sopenharmony_ci pkt_size = channels; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci latency = (buffer_size - pkt_size) / channels; 9868c2ecf20Sopenharmony_ci latency = latency * USEC_PER_SEC / 9878c2ecf20Sopenharmony_ci (params->rate_num / params->rate_den); 9888c2ecf20Sopenharmony_ci mcbsp->latency[substream->stream] = latency; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci omap_mcbsp_set_threshold(substream, pkt_size); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci dma_data->maxburst = pkt_size; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (mcbsp->configured) { 9968c2ecf20Sopenharmony_ci /* McBSP already configured by another stream */ 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7)); 10018c2ecf20Sopenharmony_ci regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7)); 10028c2ecf20Sopenharmony_ci regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); 10038c2ecf20Sopenharmony_ci regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); 10048c2ecf20Sopenharmony_ci format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK; 10058c2ecf20Sopenharmony_ci wpf = channels; 10068c2ecf20Sopenharmony_ci if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || 10078c2ecf20Sopenharmony_ci format == SND_SOC_DAIFMT_LEFT_J)) { 10088c2ecf20Sopenharmony_ci /* Use dual-phase frames */ 10098c2ecf20Sopenharmony_ci regs->rcr2 |= RPHASE; 10108c2ecf20Sopenharmony_ci regs->xcr2 |= XPHASE; 10118c2ecf20Sopenharmony_ci /* Set 1 word per (McBSP) frame for phase1 and phase2 */ 10128c2ecf20Sopenharmony_ci wpf--; 10138c2ecf20Sopenharmony_ci regs->rcr2 |= RFRLEN2(wpf - 1); 10148c2ecf20Sopenharmony_ci regs->xcr2 |= XFRLEN2(wpf - 1); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci regs->rcr1 |= RFRLEN1(wpf - 1); 10188c2ecf20Sopenharmony_ci regs->xcr1 |= XFRLEN1(wpf - 1); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci switch (params_format(params)) { 10218c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 10228c2ecf20Sopenharmony_ci /* Set word lengths */ 10238c2ecf20Sopenharmony_ci regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); 10248c2ecf20Sopenharmony_ci regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); 10258c2ecf20Sopenharmony_ci regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); 10268c2ecf20Sopenharmony_ci regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); 10278c2ecf20Sopenharmony_ci break; 10288c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 10298c2ecf20Sopenharmony_ci /* Set word lengths */ 10308c2ecf20Sopenharmony_ci regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); 10318c2ecf20Sopenharmony_ci regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); 10328c2ecf20Sopenharmony_ci regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); 10338c2ecf20Sopenharmony_ci regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); 10348c2ecf20Sopenharmony_ci break; 10358c2ecf20Sopenharmony_ci default: 10368c2ecf20Sopenharmony_ci /* Unsupported PCM format */ 10378c2ecf20Sopenharmony_ci return -EINVAL; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* In McBSP master modes, FRAME (i.e. sample rate) is generated 10418c2ecf20Sopenharmony_ci * by _counting_ BCLKs. Calculate frame size in BCLKs */ 10428c2ecf20Sopenharmony_ci master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK; 10438c2ecf20Sopenharmony_ci if (master == SND_SOC_DAIFMT_CBS_CFS) { 10448c2ecf20Sopenharmony_ci div = mcbsp->clk_div ? mcbsp->clk_div : 1; 10458c2ecf20Sopenharmony_ci framesize = (mcbsp->in_freq / div) / params_rate(params); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if (framesize < wlen * channels) { 10488c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: not enough bandwidth for desired rate and " 10498c2ecf20Sopenharmony_ci "channels\n", __func__); 10508c2ecf20Sopenharmony_ci return -EINVAL; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci } else 10538c2ecf20Sopenharmony_ci framesize = wlen * channels; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* Set FS period and length in terms of bit clock periods */ 10568c2ecf20Sopenharmony_ci regs->srgr2 &= ~FPER(0xfff); 10578c2ecf20Sopenharmony_ci regs->srgr1 &= ~FWID(0xff); 10588c2ecf20Sopenharmony_ci switch (format) { 10598c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 10608c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 10618c2ecf20Sopenharmony_ci regs->srgr2 |= FPER(framesize - 1); 10628c2ecf20Sopenharmony_ci regs->srgr1 |= FWID((framesize >> 1) - 1); 10638c2ecf20Sopenharmony_ci break; 10648c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 10658c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 10668c2ecf20Sopenharmony_ci regs->srgr2 |= FPER(framesize - 1); 10678c2ecf20Sopenharmony_ci regs->srgr1 |= FWID(0); 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs); 10728c2ecf20Sopenharmony_ci mcbsp->wlen = wlen; 10738c2ecf20Sopenharmony_ci mcbsp->configured = 1; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci return 0; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci/* 10798c2ecf20Sopenharmony_ci * This must be called before _set_clkdiv and _set_sysclk since McBSP register 10808c2ecf20Sopenharmony_ci * cache is initialized here 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_cistatic int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, 10838c2ecf20Sopenharmony_ci unsigned int fmt) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 10868c2ecf20Sopenharmony_ci struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; 10878c2ecf20Sopenharmony_ci bool inv_fs = false; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (mcbsp->configured) 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci mcbsp->fmt = fmt; 10938c2ecf20Sopenharmony_ci memset(regs, 0, sizeof(*regs)); 10948c2ecf20Sopenharmony_ci /* Generic McBSP register settings */ 10958c2ecf20Sopenharmony_ci regs->spcr2 |= XINTM(3) | FREE; 10968c2ecf20Sopenharmony_ci regs->spcr1 |= RINTM(3); 10978c2ecf20Sopenharmony_ci /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */ 10988c2ecf20Sopenharmony_ci if (!mcbsp->pdata->has_ccr) { 10998c2ecf20Sopenharmony_ci regs->rcr2 |= RFIG; 11008c2ecf20Sopenharmony_ci regs->xcr2 |= XFIG; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* Configure XCCR/RCCR only for revisions which have ccr registers */ 11048c2ecf20Sopenharmony_ci if (mcbsp->pdata->has_ccr) { 11058c2ecf20Sopenharmony_ci regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE; 11068c2ecf20Sopenharmony_ci regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 11108c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 11118c2ecf20Sopenharmony_ci /* 1-bit data delay */ 11128c2ecf20Sopenharmony_ci regs->rcr2 |= RDATDLY(1); 11138c2ecf20Sopenharmony_ci regs->xcr2 |= XDATDLY(1); 11148c2ecf20Sopenharmony_ci break; 11158c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 11168c2ecf20Sopenharmony_ci /* 0-bit data delay */ 11178c2ecf20Sopenharmony_ci regs->rcr2 |= RDATDLY(0); 11188c2ecf20Sopenharmony_ci regs->xcr2 |= XDATDLY(0); 11198c2ecf20Sopenharmony_ci regs->spcr1 |= RJUST(2); 11208c2ecf20Sopenharmony_ci /* Invert FS polarity configuration */ 11218c2ecf20Sopenharmony_ci inv_fs = true; 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 11248c2ecf20Sopenharmony_ci /* 1-bit data delay */ 11258c2ecf20Sopenharmony_ci regs->rcr2 |= RDATDLY(1); 11268c2ecf20Sopenharmony_ci regs->xcr2 |= XDATDLY(1); 11278c2ecf20Sopenharmony_ci /* Invert FS polarity configuration */ 11288c2ecf20Sopenharmony_ci inv_fs = true; 11298c2ecf20Sopenharmony_ci break; 11308c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 11318c2ecf20Sopenharmony_ci /* 0-bit data delay */ 11328c2ecf20Sopenharmony_ci regs->rcr2 |= RDATDLY(0); 11338c2ecf20Sopenharmony_ci regs->xcr2 |= XDATDLY(0); 11348c2ecf20Sopenharmony_ci /* Invert FS polarity configuration */ 11358c2ecf20Sopenharmony_ci inv_fs = true; 11368c2ecf20Sopenharmony_ci break; 11378c2ecf20Sopenharmony_ci default: 11388c2ecf20Sopenharmony_ci /* Unsupported data format */ 11398c2ecf20Sopenharmony_ci return -EINVAL; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 11438c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 11448c2ecf20Sopenharmony_ci /* McBSP master. Set FS and bit clocks as outputs */ 11458c2ecf20Sopenharmony_ci regs->pcr0 |= FSXM | FSRM | 11468c2ecf20Sopenharmony_ci CLKXM | CLKRM; 11478c2ecf20Sopenharmony_ci /* Sample rate generator drives the FS */ 11488c2ecf20Sopenharmony_ci regs->srgr2 |= FSGM; 11498c2ecf20Sopenharmony_ci break; 11508c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 11518c2ecf20Sopenharmony_ci /* McBSP slave. FS clock as output */ 11528c2ecf20Sopenharmony_ci regs->srgr2 |= FSGM; 11538c2ecf20Sopenharmony_ci regs->pcr0 |= FSXM | FSRM; 11548c2ecf20Sopenharmony_ci break; 11558c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 11568c2ecf20Sopenharmony_ci /* McBSP slave */ 11578c2ecf20Sopenharmony_ci break; 11588c2ecf20Sopenharmony_ci default: 11598c2ecf20Sopenharmony_ci /* Unsupported master/slave configuration */ 11608c2ecf20Sopenharmony_ci return -EINVAL; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* Set bit clock (CLKX/CLKR) and FS polarities */ 11648c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 11658c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 11668c2ecf20Sopenharmony_ci /* 11678c2ecf20Sopenharmony_ci * Normal BCLK + FS. 11688c2ecf20Sopenharmony_ci * FS active low. TX data driven on falling edge of bit clock 11698c2ecf20Sopenharmony_ci * and RX data sampled on rising edge of bit clock. 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_ci regs->pcr0 |= FSXP | FSRP | 11728c2ecf20Sopenharmony_ci CLKXP | CLKRP; 11738c2ecf20Sopenharmony_ci break; 11748c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 11758c2ecf20Sopenharmony_ci regs->pcr0 |= CLKXP | CLKRP; 11768c2ecf20Sopenharmony_ci break; 11778c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 11788c2ecf20Sopenharmony_ci regs->pcr0 |= FSXP | FSRP; 11798c2ecf20Sopenharmony_ci break; 11808c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 11818c2ecf20Sopenharmony_ci break; 11828c2ecf20Sopenharmony_ci default: 11838c2ecf20Sopenharmony_ci return -EINVAL; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci if (inv_fs) 11868c2ecf20Sopenharmony_ci regs->pcr0 ^= FSXP | FSRP; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return 0; 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cistatic int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, 11928c2ecf20Sopenharmony_ci int div_id, int div) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 11958c2ecf20Sopenharmony_ci struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (div_id != OMAP_MCBSP_CLKGDV) 11988c2ecf20Sopenharmony_ci return -ENODEV; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci mcbsp->clk_div = div; 12018c2ecf20Sopenharmony_ci regs->srgr1 &= ~CLKGDV(0xff); 12028c2ecf20Sopenharmony_ci regs->srgr1 |= CLKGDV(div - 1); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci return 0; 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 12088c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, 12098c2ecf20Sopenharmony_ci int dir) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 12128c2ecf20Sopenharmony_ci struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; 12138c2ecf20Sopenharmony_ci int err = 0; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (mcbsp->active) { 12168c2ecf20Sopenharmony_ci if (freq == mcbsp->in_freq) 12178c2ecf20Sopenharmony_ci return 0; 12188c2ecf20Sopenharmony_ci else 12198c2ecf20Sopenharmony_ci return -EBUSY; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci mcbsp->in_freq = freq; 12238c2ecf20Sopenharmony_ci regs->srgr2 &= ~CLKSM; 12248c2ecf20Sopenharmony_ci regs->pcr0 &= ~SCLKME; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci switch (clk_id) { 12278c2ecf20Sopenharmony_ci case OMAP_MCBSP_SYSCLK_CLK: 12288c2ecf20Sopenharmony_ci regs->srgr2 |= CLKSM; 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci case OMAP_MCBSP_SYSCLK_CLKS_FCLK: 12318c2ecf20Sopenharmony_ci if (mcbsp_omap1()) { 12328c2ecf20Sopenharmony_ci err = -EINVAL; 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci err = omap2_mcbsp_set_clks_src(mcbsp, 12368c2ecf20Sopenharmony_ci MCBSP_CLKS_PRCM_SRC); 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci case OMAP_MCBSP_SYSCLK_CLKS_EXT: 12398c2ecf20Sopenharmony_ci if (mcbsp_omap1()) { 12408c2ecf20Sopenharmony_ci err = 0; 12418c2ecf20Sopenharmony_ci break; 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci err = omap2_mcbsp_set_clks_src(mcbsp, 12448c2ecf20Sopenharmony_ci MCBSP_CLKS_PAD_SRC); 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci case OMAP_MCBSP_SYSCLK_CLKX_EXT: 12488c2ecf20Sopenharmony_ci regs->srgr2 |= CLKSM; 12498c2ecf20Sopenharmony_ci regs->pcr0 |= SCLKME; 12508c2ecf20Sopenharmony_ci /* 12518c2ecf20Sopenharmony_ci * If McBSP is master but yet the CLKX/CLKR pin drives the SRG, 12528c2ecf20Sopenharmony_ci * disable output on those pins. This enables to inject the 12538c2ecf20Sopenharmony_ci * reference clock through CLKX/CLKR. For this to work 12548c2ecf20Sopenharmony_ci * set_dai_sysclk() _needs_ to be called after set_dai_fmt(). 12558c2ecf20Sopenharmony_ci */ 12568c2ecf20Sopenharmony_ci regs->pcr0 &= ~CLKXM; 12578c2ecf20Sopenharmony_ci break; 12588c2ecf20Sopenharmony_ci case OMAP_MCBSP_SYSCLK_CLKR_EXT: 12598c2ecf20Sopenharmony_ci regs->pcr0 |= SCLKME; 12608c2ecf20Sopenharmony_ci /* Disable ouput on CLKR pin in master mode */ 12618c2ecf20Sopenharmony_ci regs->pcr0 &= ~CLKRM; 12628c2ecf20Sopenharmony_ci break; 12638c2ecf20Sopenharmony_ci default: 12648c2ecf20Sopenharmony_ci err = -ENODEV; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return err; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops mcbsp_dai_ops = { 12718c2ecf20Sopenharmony_ci .startup = omap_mcbsp_dai_startup, 12728c2ecf20Sopenharmony_ci .shutdown = omap_mcbsp_dai_shutdown, 12738c2ecf20Sopenharmony_ci .prepare = omap_mcbsp_dai_prepare, 12748c2ecf20Sopenharmony_ci .trigger = omap_mcbsp_dai_trigger, 12758c2ecf20Sopenharmony_ci .delay = omap_mcbsp_dai_delay, 12768c2ecf20Sopenharmony_ci .hw_params = omap_mcbsp_dai_hw_params, 12778c2ecf20Sopenharmony_ci .set_fmt = omap_mcbsp_dai_set_dai_fmt, 12788c2ecf20Sopenharmony_ci .set_clkdiv = omap_mcbsp_dai_set_clkdiv, 12798c2ecf20Sopenharmony_ci .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, 12808c2ecf20Sopenharmony_ci}; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic int omap_mcbsp_probe(struct snd_soc_dai *dai) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci pm_runtime_enable(mcbsp->dev); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, 12898c2ecf20Sopenharmony_ci &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK], 12908c2ecf20Sopenharmony_ci &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci return 0; 12938c2ecf20Sopenharmony_ci} 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_cistatic int omap_mcbsp_remove(struct snd_soc_dai *dai) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci pm_runtime_disable(mcbsp->dev); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci return 0; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver omap_mcbsp_dai = { 13058c2ecf20Sopenharmony_ci .probe = omap_mcbsp_probe, 13068c2ecf20Sopenharmony_ci .remove = omap_mcbsp_remove, 13078c2ecf20Sopenharmony_ci .playback = { 13088c2ecf20Sopenharmony_ci .channels_min = 1, 13098c2ecf20Sopenharmony_ci .channels_max = 16, 13108c2ecf20Sopenharmony_ci .rates = OMAP_MCBSP_RATES, 13118c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 13128c2ecf20Sopenharmony_ci }, 13138c2ecf20Sopenharmony_ci .capture = { 13148c2ecf20Sopenharmony_ci .channels_min = 1, 13158c2ecf20Sopenharmony_ci .channels_max = 16, 13168c2ecf20Sopenharmony_ci .rates = OMAP_MCBSP_RATES, 13178c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 13188c2ecf20Sopenharmony_ci }, 13198c2ecf20Sopenharmony_ci .ops = &mcbsp_dai_ops, 13208c2ecf20Sopenharmony_ci}; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver omap_mcbsp_component = { 13238c2ecf20Sopenharmony_ci .name = "omap-mcbsp", 13248c2ecf20Sopenharmony_ci}; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_cistatic struct omap_mcbsp_platform_data omap2420_pdata = { 13278c2ecf20Sopenharmony_ci .reg_step = 4, 13288c2ecf20Sopenharmony_ci .reg_size = 2, 13298c2ecf20Sopenharmony_ci}; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_cistatic struct omap_mcbsp_platform_data omap2430_pdata = { 13328c2ecf20Sopenharmony_ci .reg_step = 4, 13338c2ecf20Sopenharmony_ci .reg_size = 4, 13348c2ecf20Sopenharmony_ci .has_ccr = true, 13358c2ecf20Sopenharmony_ci}; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic struct omap_mcbsp_platform_data omap3_pdata = { 13388c2ecf20Sopenharmony_ci .reg_step = 4, 13398c2ecf20Sopenharmony_ci .reg_size = 4, 13408c2ecf20Sopenharmony_ci .has_ccr = true, 13418c2ecf20Sopenharmony_ci .has_wakeup = true, 13428c2ecf20Sopenharmony_ci}; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_cistatic struct omap_mcbsp_platform_data omap4_pdata = { 13458c2ecf20Sopenharmony_ci .reg_step = 4, 13468c2ecf20Sopenharmony_ci .reg_size = 4, 13478c2ecf20Sopenharmony_ci .has_ccr = true, 13488c2ecf20Sopenharmony_ci .has_wakeup = true, 13498c2ecf20Sopenharmony_ci}; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic const struct of_device_id omap_mcbsp_of_match[] = { 13528c2ecf20Sopenharmony_ci { 13538c2ecf20Sopenharmony_ci .compatible = "ti,omap2420-mcbsp", 13548c2ecf20Sopenharmony_ci .data = &omap2420_pdata, 13558c2ecf20Sopenharmony_ci }, 13568c2ecf20Sopenharmony_ci { 13578c2ecf20Sopenharmony_ci .compatible = "ti,omap2430-mcbsp", 13588c2ecf20Sopenharmony_ci .data = &omap2430_pdata, 13598c2ecf20Sopenharmony_ci }, 13608c2ecf20Sopenharmony_ci { 13618c2ecf20Sopenharmony_ci .compatible = "ti,omap3-mcbsp", 13628c2ecf20Sopenharmony_ci .data = &omap3_pdata, 13638c2ecf20Sopenharmony_ci }, 13648c2ecf20Sopenharmony_ci { 13658c2ecf20Sopenharmony_ci .compatible = "ti,omap4-mcbsp", 13668c2ecf20Sopenharmony_ci .data = &omap4_pdata, 13678c2ecf20Sopenharmony_ci }, 13688c2ecf20Sopenharmony_ci { }, 13698c2ecf20Sopenharmony_ci}; 13708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_mcbsp_of_match); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic int asoc_mcbsp_probe(struct platform_device *pdev) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev); 13758c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp; 13768c2ecf20Sopenharmony_ci const struct of_device_id *match; 13778c2ecf20Sopenharmony_ci int ret; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci match = of_match_device(omap_mcbsp_of_match, &pdev->dev); 13808c2ecf20Sopenharmony_ci if (match) { 13818c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 13828c2ecf20Sopenharmony_ci struct omap_mcbsp_platform_data *pdata_quirk = pdata; 13838c2ecf20Sopenharmony_ci int buffer_size; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, 13868c2ecf20Sopenharmony_ci sizeof(struct omap_mcbsp_platform_data), 13878c2ecf20Sopenharmony_ci GFP_KERNEL); 13888c2ecf20Sopenharmony_ci if (!pdata) 13898c2ecf20Sopenharmony_ci return -ENOMEM; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci memcpy(pdata, match->data, sizeof(*pdata)); 13928c2ecf20Sopenharmony_ci if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size)) 13938c2ecf20Sopenharmony_ci pdata->buffer_size = buffer_size; 13948c2ecf20Sopenharmony_ci if (pdata_quirk) 13958c2ecf20Sopenharmony_ci pdata->force_ick_on = pdata_quirk->force_ick_on; 13968c2ecf20Sopenharmony_ci } else if (!pdata) { 13978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing platform data.\n"); 13988c2ecf20Sopenharmony_ci return -EINVAL; 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL); 14018c2ecf20Sopenharmony_ci if (!mcbsp) 14028c2ecf20Sopenharmony_ci return -ENOMEM; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci mcbsp->id = pdev->id; 14058c2ecf20Sopenharmony_ci mcbsp->pdata = pdata; 14068c2ecf20Sopenharmony_ci mcbsp->dev = &pdev->dev; 14078c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mcbsp); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci ret = omap_mcbsp_init(pdev); 14108c2ecf20Sopenharmony_ci if (ret) 14118c2ecf20Sopenharmony_ci return ret; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci if (mcbsp->pdata->reg_size == 2) { 14148c2ecf20Sopenharmony_ci omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE; 14158c2ecf20Sopenharmony_ci omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 14198c2ecf20Sopenharmony_ci &omap_mcbsp_component, 14208c2ecf20Sopenharmony_ci &omap_mcbsp_dai, 1); 14218c2ecf20Sopenharmony_ci if (ret) 14228c2ecf20Sopenharmony_ci return ret; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci return sdma_pcm_platform_register(&pdev->dev, "tx", "rx"); 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic int asoc_mcbsp_remove(struct platform_device *pdev) 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (mcbsp->pdata->ops && mcbsp->pdata->ops->free) 14328c2ecf20Sopenharmony_ci mcbsp->pdata->ops->free(mcbsp->id); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (cpu_latency_qos_request_active(&mcbsp->pm_qos_req)) 14358c2ecf20Sopenharmony_ci cpu_latency_qos_remove_request(&mcbsp->pm_qos_req); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (mcbsp->pdata->buffer_size) 14388c2ecf20Sopenharmony_ci sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci omap_mcbsp_st_cleanup(pdev); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci return 0; 14438c2ecf20Sopenharmony_ci} 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_cistatic struct platform_driver asoc_mcbsp_driver = { 14468c2ecf20Sopenharmony_ci .driver = { 14478c2ecf20Sopenharmony_ci .name = "omap-mcbsp", 14488c2ecf20Sopenharmony_ci .of_match_table = omap_mcbsp_of_match, 14498c2ecf20Sopenharmony_ci }, 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci .probe = asoc_mcbsp_probe, 14528c2ecf20Sopenharmony_ci .remove = asoc_mcbsp_remove, 14538c2ecf20Sopenharmony_ci}; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cimodule_platform_driver(asoc_mcbsp_driver); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>"); 14588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMAP I2S SoC Interface"); 14598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 14608c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:omap-mcbsp"); 1461