X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fsound%2Fsamsung-i2s.c;h=104584073af4f2c8db5b9b1bab8709fd6c9bf3b7;hb=9ad996adcc135bc34b185957567c8c46deb90d4f;hp=9f3117dd942012ae630c09ef57befb1af6212475;hpb=2c601c7208713ba9b2158c57adcf515f4bdbc212;p=oweals%2Fu-boot.git diff --git a/drivers/sound/samsung-i2s.c b/drivers/sound/samsung-i2s.c index 9f3117dd94..104584073a 100644 --- a/drivers/sound/samsung-i2s.c +++ b/drivers/sound/samsung-i2s.c @@ -1,33 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Samsung Electronics * R. Chandrasekar - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA */ +#include +#include +#include +#include #include #include #include #include -#include -#include -#include #define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) #define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) @@ -40,7 +24,7 @@ /* * Sets the frame size for I2S LR clock * - * @param i2s_reg i2s regiter address + * @param i2s_reg i2s register address * @param rfs Frame Size */ static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs) @@ -70,7 +54,7 @@ static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs) /* * Sets the i2s transfer control * - * @param i2s_reg i2s regiter address + * @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) @@ -81,9 +65,7 @@ static void i2s_txctrl(struct i2s_reg *i2s_reg, int on) if (on) { con |= CON_ACTIVE; con &= ~CON_TXCH_PAUSE; - } else { - con |= CON_TXCH_PAUSE; con &= ~CON_ACTIVE; } @@ -95,7 +77,7 @@ static void i2s_txctrl(struct i2s_reg *i2s_reg, int on) /* * set the bit clock frame size (in multiples of LRCLK) * - * @param i2s_reg i2s regiter address + * @param i2s_reg i2s register address * @param bfs bit Frame Size */ static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs) @@ -126,11 +108,11 @@ static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs) /* * flushes the i2stx fifo * - * @param i2s_reg i2s regiter address + * @param i2s_reg i2s register address * @param flush Tx fifo flush command (0x00 - do not flush * 0x80 - flush tx fifo) */ -void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) +static void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) { /* Flush the FIFO */ setbits_le32(&i2s_reg->fic, flush); @@ -140,12 +122,12 @@ void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) /* * Set System Clock direction * - * @param i2s_reg i2s regiter address + * @param i2s_reg i2s register address * @param dir Clock direction * * @return int value 0 for success, -1 in case of error */ -int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) +static int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) { unsigned int mod = readl(&i2s_reg->mod); @@ -163,11 +145,11 @@ int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) * Sets I2S Clcok format * * @param fmt i2s clock properties - * @param i2s_reg i2s regiter address + * @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) +static int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) { unsigned int mod = readl(&i2s_reg->mod); unsigned int tmp = 0; @@ -188,8 +170,8 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) break; default: debug("%s: Invalid format priority [0x%x]\n", __func__, - (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); - return -1; + (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); + return -ERANGE; } /* @@ -207,8 +189,8 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) break; default: debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, - (fmt & SND_SOC_DAIFMT_INV_MASK)); - return -1; + (fmt & SND_SOC_DAIFMT_INV_MASK)); + return -ERANGE; } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -220,13 +202,13 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT); if (ret != 0) { debug("%s:set i2s clock direction failed\n", __func__); - return -1; + return ret; } break; default: debug("%s: Invalid master selection [0x%x]\n", __func__, - (fmt & SND_SOC_DAIFMT_MASTER_MASK)); - return -1; + (fmt & SND_SOC_DAIFMT_MASTER_MASK)); + return -ERANGE; } mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); @@ -240,11 +222,11 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) * Sets the sample width in bits * * @param blc samplewidth (size of sample in bits) - * @param i2s_reg i2s regiter address + * @param i2s_reg i2s register address * * @return int value 0 for success, -1 in case of error */ -int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) +static int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) { unsigned int mod = readl(&i2s_reg->mod); @@ -266,44 +248,44 @@ int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) break; default: debug("%s: Invalid sample size input [0x%x]\n", - __func__, blc); - return -1; + __func__, blc); + return -ERANGE; } writel(mod, &i2s_reg->mod); return 0; } -int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data, - unsigned long data_size) +int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, void *data, + uint data_size) { + struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; + u32 *ptr; int i; int start; - struct i2s_reg *i2s_reg = - (struct i2s_reg *)pi2s_tx->base_address; if (data_size < FIFO_LENGTH) { debug("%s : Invalid data size\n", __func__); - return -1; /* invalid pcm data size */ + return -ENODATA; /* invalid pcm data size */ } /* fill the tx buffer before stating the tx transmit */ - for (i = 0; i < FIFO_LENGTH; i++) - writel(*data++, &i2s_reg->txd); + for (i = 0, ptr = data; i < FIFO_LENGTH; i++) + writel(*ptr++, &i2s_reg->txd); - data_size -= FIFO_LENGTH; + data_size -= sizeof(*ptr) * FIFO_LENGTH; i2s_txctrl(i2s_reg, I2S_TX_ON); while (data_size > 0) { start = get_timer(0); if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) { - writel(*data++, &i2s_reg->txd); - data_size--; + writel(*ptr++, &i2s_reg->txd); + data_size -= sizeof(*ptr); } else { if (get_timer(start) > TIMEOUT_I2S_TX) { i2s_txctrl(i2s_reg, I2S_TX_OFF); debug("%s: I2S Transfer Timeout\n", __func__); - return -1; + return -ETIMEDOUT; } } } @@ -312,38 +294,69 @@ int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data, return 0; } -int i2s_tx_init(struct i2stx_info *pi2s_tx) +static int i2s_tx_init(struct i2s_uc_priv *pi2s_tx) { int ret; - struct i2s_reg *i2s_reg = - (struct i2s_reg *)pi2s_tx->base_address; + struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; + + if (pi2s_tx->id == 0) { + /* Initialize GPIO for I2S-0 */ + exynos_pinmux_config(PERIPH_ID_I2S0, 0); + + /* Set EPLL Clock */ + ret = set_epll_clk(pi2s_tx->samplingrate * pi2s_tx->rfs * 4); + } else if (pi2s_tx->id == 1) { + /* Initialize GPIO for I2S-1 */ + exynos_pinmux_config(PERIPH_ID_I2S1, 0); + + /* Set EPLL Clock */ + ret = set_epll_clk(pi2s_tx->audio_pll_clk); + } else { + debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id); + return -ERANGE; + } - /* Initialize GPIO for I2s */ - exynos_pinmux_config(PERIPH_ID_I2S1, 0); + if (ret) { + debug("%s: epll clock set rate failed\n", __func__); + return ret; + } - /* Set EPLL Clock */ - ret = set_epll_clk(pi2s_tx->audio_pll_clk); - if (ret != 0) { - debug("%s: epll clock set rate falied\n", __func__); - return -1; + /* Select Clk Source for Audio 0 or 1 */ + ret = set_i2s_clk_source(pi2s_tx->id); + if (ret) { + debug("%s: unsupported clock for i2s-%d\n", __func__, + pi2s_tx->id); + return ret; } - /* Select Clk Source for Audio1 */ - set_i2s_clk_source(); + if (pi2s_tx->id == 0) { + /*Reset the i2s module */ + writel(CON_RESET, &i2s_reg->con); - /* Set Prescaler to get MCLK */ - set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, - (pi2s_tx->samplingrate * (pi2s_tx->rfs))); + writel(MOD_OP_CLK | MOD_RCLKSRC, &i2s_reg->mod); + /* set i2s prescaler */ + writel(PSREN | PSVAL, &i2s_reg->psr); + } else { + /* Set Prescaler to get MCLK */ + ret = set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->id); + } + if (ret) { + debug("%s: unsupported prescalar for i2s-%d\n", __func__, + pi2s_tx->id); + return ret; + } /* Configure I2s format */ - ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM)); + ret = i2s_set_fmt(i2s_reg, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); if (ret == 0) { i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); if (ret != 0) { debug("%s:set sample rate failed\n", __func__); - return -1; + return ret; } i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs); @@ -356,3 +369,87 @@ int i2s_tx_init(struct i2stx_info *pi2s_tx) return ret; } + +static int samsung_i2s_tx_data(struct udevice *dev, void *data, uint data_size) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + + return i2s_transfer_tx_data(priv, data, data_size); +} + +static int samsung_i2s_probe(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + + return i2s_tx_init(priv); +} + +static int samsung_i2s_ofdata_to_platdata(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + ulong base; + + /* + * 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 = dev_read_addr(dev); + if (base == FDT_ADDR_T_NONE) { + debug("%s: Missing i2s base\n", __func__); + return -EINVAL; + } + priv->base_address = base; + + if (dev_read_u32u(dev, "samsung,i2s-epll-clock-frequency", + &priv->audio_pll_clk)) + goto err; + debug("audio_pll_clk = %d\n", priv->audio_pll_clk); + if (dev_read_u32u(dev, "samsung,i2s-sampling-rate", + &priv->samplingrate)) + goto err; + debug("samplingrate = %d\n", priv->samplingrate); + if (dev_read_u32u(dev, "samsung,i2s-bits-per-sample", + &priv->bitspersample)) + goto err; + debug("bitspersample = %d\n", priv->bitspersample); + if (dev_read_u32u(dev, "samsung,i2s-channels", &priv->channels)) + goto err; + debug("channels = %d\n", priv->channels); + if (dev_read_u32u(dev, "samsung,i2s-lr-clk-framesize", &priv->rfs)) + goto err; + debug("rfs = %d\n", priv->rfs); + if (dev_read_u32u(dev, "samsung,i2s-bit-clk-framesize", &priv->bfs)) + goto err; + debug("bfs = %d\n", priv->bfs); + + if (dev_read_u32u(dev, "samsung,i2s-id", &priv->id)) + goto err; + debug("id = %d\n", priv->id); + + return 0; + +err: + debug("fail to get sound i2s node properties\n"); + + return -EINVAL; +} + +static const struct i2s_ops samsung_i2s_ops = { + .tx_data = samsung_i2s_tx_data, +}; + +static const struct udevice_id samsung_i2s_ids[] = { + { .compatible = "samsung,s5pv210-i2s" }, + { } +}; + +U_BOOT_DRIVER(samsung_i2s) = { + .name = "samsung_i2s", + .id = UCLASS_I2S, + .of_match = samsung_i2s_ids, + .probe = samsung_i2s_probe, + .ofdata_to_platdata = samsung_i2s_ofdata_to_platdata, + .ops = &samsung_i2s_ops, +};