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