blob: a9d0dc0ab5f1e2061a64a073c9f2b23a172ae945 [file] [log] [blame]
/*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Portions Copyright (C) 2012 Samsung Electronics
* R. Chandrasekar <rcsekar@samsung.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/ahub.h>
#include <asm/arch/clk_rst.h>
#include <asm/arch/clock.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/i2s.h>
#include <sound.h>
#include <i2s.h>
#include <libfdt.h>
#include <fdtdec.h>
#include "tegra-ahub.h"
/*
* This is the Tegra30/Tegra114 I2S driver used by the 'sound' command.
* I2S on Tegra30+ connects to the AHUB (Audio Hub) on one end, and the
* audio codec (RealTek, Maxim, Wolfson, etc.) on the other, at least
* as far as this code and U-Boot 'sound' is concerned. The I2S driver
* will send an audio stream (PCM, 16-bit stereo) to the codec.
*/
/*
* Sample rate calculations:
* For LRCK mode, the channel_bit_cnt can be calculated as:
* channel_bit_cnt = (freq of bit_clk) / (2 * sample rate) - 1
* For FSYNC mode, the channel_bit_cnt can be calculated as:
* channel_bit_cnt = (freq of bit_clk) / (sample rate) - 1
*/
/*
* Sets the i2s transfer control
*
* @param i2s_reg i2s register address
* @param on 1 enable tx , 0 disable tx transfer
*/
static void i2s_txctrl(struct i2s_reg *i2s_reg, int on)
{
debug("%s called, tx on = %d\n", __func__, on);
/* Set I2S_CTRL[31] = 1 to enable I2S Tx mode */
clrsetbits_le32(&i2s_reg->i2s_ctrl, (1 << 31), (on << 31));
debug("%s: Wrote 0x%08X to I2S_CTRL reg\n", __func__,
readl(&i2s_reg->i2s_ctrl));
}
/*
* Sets I2S Clock format
*
* @param fmt i2s clock properties
* @param i2s_reg i2s register address
*
* @return int value 0 for success, -1 in case of error
*/
int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt)
{
u32 i2sctrl;
debug("%s called, i2s clock props = 0x%04X\n", __func__, fmt);
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
debug("%s: Format is NB_NF\n", __func__);
break;
default:
return -1;
}
i2sctrl = readl(&i2s_reg->i2s_ctrl);
i2sctrl &= ~I2S_CTRL_MASTER_ENABLE;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
debug("%s: Format is CBS_CFS\n", __func__);
i2sctrl |= I2S_CTRL_MASTER_ENABLE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
debug("%s: Format is CBM_CFM\n", __func__);
break;
default:
return -1;
}
i2sctrl &= ~(I2S_CTRL_FRAME_FORMAT_MASK | I2S_CTRL_LRCK_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
debug("%s: Format is DSP_A\n", __func__);
i2sctrl |= I2S_CTRL_FRAME_FORMAT_FSYNC;
i2sctrl |= I2S_CTRL_LRCK_L_LOW;
break;
case SND_SOC_DAIFMT_DSP_B:
debug("%s: Format is DSP_B\n", __func__);
i2sctrl |= I2S_CTRL_FRAME_FORMAT_FSYNC;
i2sctrl |= I2S_CTRL_LRCK_R_LOW;
break;
case SND_SOC_DAIFMT_I2S:
debug("%s: Format is I2S\n", __func__);
i2sctrl |= I2S_CTRL_FRAME_FORMAT_LRCK;
i2sctrl |= I2S_CTRL_LRCK_L_LOW;
break;
case SND_SOC_DAIFMT_RIGHT_J:
debug("%s: Format is RJ\n", __func__);
i2sctrl |= I2S_CTRL_FRAME_FORMAT_LRCK;
i2sctrl |= I2S_CTRL_LRCK_L_LOW;
break;
case SND_SOC_DAIFMT_LEFT_J:
debug("%s: Format is LJ\n", __func__);
i2sctrl |= I2S_CTRL_FRAME_FORMAT_LRCK;
i2sctrl |= I2S_CTRL_LRCK_L_LOW;
break;
default:
return -1;
}
writel(i2sctrl, &i2s_reg->i2s_ctrl);
debug("%s: Wrote 0x%08X to I2S_CTRL\n", __func__, i2sctrl);
return 0;
}
/*
* Instead of using DMA, transfer sq wave data to TXFIFO w/FIFO full check.
* TODO(twarren@nvidia.com): Use APBDMA
*/
int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data,
unsigned long data_size)
{
int i, start;
struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address;
struct ahub_ctlr *ahub = (struct ahub_ctlr *)NV_PA_APBIF_BASE;
debug("%s called, data size = %lu\n", __func__, data_size);
if (data_size < FIFO_LENGTH) {
debug("%s : Invalid data size\n", __func__);
return -1; /* invalid pcm data size */
}
ahub_init(pi2s_tx->bitspersample, pi2s_tx->channels);
/* Fill the TX buffer before starting the TX transmit */
for (i = 0; i < FIFO_LENGTH; i++)
writel(*data++, &ahub->ahub_channel0_txfifo);
data_size -= FIFO_LENGTH;
i2s_txctrl(i2s_reg, I2S_TX_ON);
while (data_size > 0) {
start = get_timer(0);
if (!(I2S1_TX_FIFO_FULL & readl(&ahub->ahub_i2s_live_stat))) {
writel(*data++, &ahub->ahub_channel0_txfifo);
data_size--;
} else {
debug("%s: FIFO full: data_size %lu\n", __func__,
data_size);
if (get_timer(start) > TIMEOUT_I2S_TX) {
i2s_txctrl(i2s_reg, I2S_TX_OFF);
debug("%s: I2S Transfer Timeout\n", __func__);
return -1;
}
}
}
i2s_txctrl(i2s_reg, I2S_TX_OFF);
return 0;
}
int i2s_tx_init(struct i2stx_info *pi2s_tx)
{
int ret;
struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address;
unsigned int i2s_rate, chan_bit_cnt, reg, audio_bits, tmp;
debug("%s called, base addr = 0x%08X\n",
__func__, pi2s_tx->base_address);
/*
* Setup I2S clock, 4.8MHz using PLLP.
* NOTE: clock_xxx functions for Tegra are not fully
* cognizant of PLLA and it's ref clock of 9.6MHz,
* so we can't use them for this (yet). We use PLLP
* instead.
*/
i2s_rate = clock_start_periph_pll(pi2s_tx->periph_id, CLOCK_ID_PERIPH,
pi2s_tx->audio_pll_clk);
debug("%s: I2S clock rate s/b= %d\n", __func__, i2s_rate);
debug("%s: I2S clk src reg = 0x%08X\n", __func__, readl(0x60006100));
debug("%s: PLLP = %d\n", __func__, clock_get_rate(CLOCK_ID_PERIPH));
debug("%s: PLLA = %d\n", __func__, clock_get_rate(CLOCK_ID_AUDIO));
debug("%s: OSC = %d\n", __func__, clock_get_rate(CLOCK_ID_OSC));
ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM));
if (ret == 0) {
/* disable i2s transfer flag */
i2s_txctrl(i2s_reg, I2S_TX_OFF);
} else {
debug("%s: failed\n", __func__);
}
/* I2S_CTRL (except XFER_EN) */
audio_bits = (pi2s_tx->bitspersample >> 2) - 1;
reg = audio_bits | (1 << 10); /* MASTER */
writel(reg, &i2s_reg->i2s_ctrl);
/* I2S_TIMING */
chan_bit_cnt = (pi2s_tx->audio_pll_clk / (pi2s_tx->samplingrate*2)) - 1;
writel(chan_bit_cnt, &i2s_reg->i2s_timing);
/* I2S_OFFSET */
reg = 0x00010001; /* RX/TX_DATA_OFFSET = 1 bit clock */
writel(reg, &i2s_reg->i2s_offset);
/* I2S_CH_CTRL */
reg = (1 << 24); /* FSYNC_WIDTH = 2 clocks wide */
writel(reg, &i2s_reg->i2s_ch_ctrl);
/* I2S_SLOT_CTRL */
reg = 1; /* TOTAL_SLOTS = 2 slots per fsync */
writel(reg, &i2s_reg->i2s_slot_ctrl);
/* I2S_AUDIOCIF_I2STX_CTRL */
tmp = pi2s_tx->channels - 1;
reg = (tmp << 24) | (tmp << 16);
reg |= (audio_bits << 12) | (audio_bits << 8);
reg |= (4 << 28); /* THRESHOLD */
writel(reg, &i2s_reg->i2s_cif_tx_ctrl);
reg |= (1 << 2); /* DIRECTION = RXCIF */
writel(reg, &i2s_reg->i2s_cif_tx_ctrl);
return 0;
}
/*
* get_sound_i2s_values gets values for I2S parameters from DT
*
* @param i2stx_info I2S transmitter transfer param structure
* @param blob FDT blob if enabled else NULL
*/
int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob)
{
int node, error = 0;
unsigned int base;
node = fdtdec_next_compatible(blob, 0,
COMPAT_NVIDIA_TEGRA114_SOUND);
if (node <= 0) {
debug("Tegra, sound: No node for sound in device tree\n");
return -1;
}
/*
* Get the pre-defined sound specific values from FDT.
* All of these are expected to be correct otherwise
* wrong register values in I2S setup parameters may
* result in no sound play.
*/
base = fdtdec_get_addr(blob, node, "reg");
if (base == FDT_ADDR_T_NONE) {
debug("%s: Missing I2S reg base address\n", __func__);
return -1;
}
i2s->base_address = base;
i2s->audio_pll_clk = fdtdec_get_int(blob, node,
"nvidia,i2s-pll-clock-frequency", -1);
error |= i2s->audio_pll_clk;
i2s->samplingrate = fdtdec_get_int(blob, node,
"nvidia,i2s-sampling-rate", -1);
error |= i2s->samplingrate;
i2s->bitspersample = fdtdec_get_int(blob, node,
"nvidia,i2s-bits-per-sample", -1);
error |= i2s->bitspersample;
i2s->channels = fdtdec_get_int(blob, node,
"nvidia,i2s-channels", -1);
error |= i2s->channels;
i2s->rfs = fdtdec_get_int(blob, node,
"nvidia,i2s-lr-clk-framesize", -1);
error |= i2s->rfs;
i2s->bfs = fdtdec_get_int(blob, node,
"nvidia,i2s-bit-clk-framesize", -1);
error |= i2s->bfs;
i2s->periph_id = clock_decode_periph_id(blob, node);
if (error == -1) {
debug("%s: failed to get I2S node properties\n", __func__);
return -1;
}
debug("I2S base addr = 0x%08X\n", base);
debug("periph_id = %d\n", i2s->periph_id);
debug("audio_pll_clk = %d\n", i2s->audio_pll_clk);
debug("samplingrate = %d\n", i2s->samplingrate);
debug("bitspersample = %d\n", i2s->bitspersample);
debug("channels = %d\n", i2s->channels);
debug("rfs = %d\n", i2s->rfs);
debug("bfs = %d\n", i2s->bfs);
return 0;
}