Merge branch 'master' of git://git.denx.de/u-boot-arm
This commit is contained in:
@@ -42,14 +42,14 @@ static unsigned long gpio_ports[] = {
|
||||
[1] = GPIO2_BASE_ADDR,
|
||||
[2] = GPIO3_BASE_ADDR,
|
||||
#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
|
||||
defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
|
||||
defined(CONFIG_MX53) || defined(CONFIG_MX6)
|
||||
[3] = GPIO4_BASE_ADDR,
|
||||
#endif
|
||||
#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
|
||||
#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6)
|
||||
[4] = GPIO5_BASE_ADDR,
|
||||
[5] = GPIO6_BASE_ADDR,
|
||||
#endif
|
||||
#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
|
||||
#if defined(CONFIG_MX53) || defined(CONFIG_MX6)
|
||||
[6] = GPIO7_BASE_ADDR,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -144,9 +144,11 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
|
||||
|
||||
struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio)
|
||||
{
|
||||
int bank = gpio / GPIO_PER_BANK;
|
||||
bank *= sizeof(struct s5p_gpio_bank);
|
||||
int bank;
|
||||
unsigned g = gpio - s5p_gpio_part_max(gpio);
|
||||
|
||||
bank = g / GPIO_PER_BANK;
|
||||
bank *= sizeof(struct s5p_gpio_bank);
|
||||
return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ static struct keyb {
|
||||
struct kbc_tegra *kbc; /* tegra keyboard controller */
|
||||
unsigned char inited; /* 1 if keyboard has been inited */
|
||||
unsigned char first_scan; /* 1 if this is our first key scan */
|
||||
unsigned char created; /* 1 if driver has been created */
|
||||
|
||||
/*
|
||||
* After init we must wait a short time before polling the keyboard.
|
||||
@@ -306,6 +307,10 @@ static void tegra_kbc_open(void)
|
||||
*/
|
||||
static int init_tegra_keyboard(void)
|
||||
{
|
||||
/* check if already created */
|
||||
if (config.created)
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_OF_CONTROL
|
||||
int node;
|
||||
|
||||
@@ -349,6 +354,7 @@ static int init_tegra_keyboard(void)
|
||||
config_kbc_gpio(config.kbc);
|
||||
|
||||
tegra_kbc_open();
|
||||
config.created = 1;
|
||||
debug("%s: Tegra keyboard ready\n", __func__);
|
||||
|
||||
return 0;
|
||||
@@ -357,6 +363,8 @@ static int init_tegra_keyboard(void)
|
||||
int drv_keyboard_init(void)
|
||||
{
|
||||
struct stdio_dev dev;
|
||||
char *stdinname = getenv("stdin");
|
||||
int error;
|
||||
|
||||
if (input_init(&config.input, 0)) {
|
||||
debug("%s: Cannot set up input\n", __func__);
|
||||
@@ -372,5 +380,13 @@ int drv_keyboard_init(void)
|
||||
dev.start = init_tegra_keyboard;
|
||||
|
||||
/* Register the device. init_tegra_keyboard() will be called soon */
|
||||
return input_stdio_register(&dev);
|
||||
error = input_stdio_register(&dev);
|
||||
if (error)
|
||||
return error;
|
||||
#ifdef CONFIG_CONSOLE_MUX
|
||||
error = iomux_doenv(stdin, stdinname);
|
||||
if (error)
|
||||
return error;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -565,10 +565,11 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)
|
||||
mmc->getcd = tegra_mmc_getcd;
|
||||
|
||||
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
|
||||
mmc->host_caps = 0;
|
||||
if (bus_width == 8)
|
||||
mmc->host_caps = MMC_MODE_8BIT;
|
||||
else
|
||||
mmc->host_caps = MMC_MODE_4BIT;
|
||||
mmc->host_caps |= MMC_MODE_8BIT;
|
||||
if (bus_width >= 4)
|
||||
mmc->host_caps |= MMC_MODE_4BIT;
|
||||
mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
|
||||
|
||||
/*
|
||||
|
||||
@@ -28,6 +28,7 @@ LIB := $(obj)libpmic.o
|
||||
COBJS-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
|
||||
COBJS-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
|
||||
COBJS-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o
|
||||
COBJS-$(CONFIG_POWER_MAX77686) += pmic_max77686.o
|
||||
|
||||
COBJS := $(COBJS-y)
|
||||
SRCS := $(COBJS:.o=.c)
|
||||
|
||||
48
drivers/power/pmic/pmic_max77686.c
Normal file
48
drivers/power/pmic/pmic_max77686.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Rajeshwari Shinde <rajeshwari.s@samsung.com>
|
||||
*
|
||||
* 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 <common.h>
|
||||
#include <power/pmic.h>
|
||||
#include <power/max77686_pmic.h>
|
||||
#include <errno.h>
|
||||
|
||||
int pmic_init(unsigned char bus)
|
||||
{
|
||||
static const char name[] = "MAX77686_PMIC";
|
||||
struct pmic *p = pmic_alloc();
|
||||
|
||||
if (!p) {
|
||||
printf("%s: POWER allocation error!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
puts("Board PMIC init\n");
|
||||
p->name = name;
|
||||
p->interface = PMIC_I2C;
|
||||
p->number_of_regs = PMIC_NUM_OF_REGS;
|
||||
p->hw.i2c.addr = MAX77686_I2C_ADDR;
|
||||
p->hw.i2c.tx_num = 1;
|
||||
p->bus = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -27,6 +27,12 @@
|
||||
#include <fsl_pmic.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(CONFIG_PMIC_FSL_MC13892)
|
||||
#define FSL_PMIC_I2C_LENGTH 3
|
||||
#elif defined(CONFIG_PMIC_FSL_MC34704)
|
||||
#define FSL_PMIC_I2C_LENGTH 1
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_POWER_SPI)
|
||||
static u32 pmic_spi_prepare_tx(u32 reg, u32 *val, u32 write)
|
||||
{
|
||||
@@ -59,7 +65,7 @@ int pmic_init(unsigned char bus)
|
||||
#elif defined(CONFIG_POWER_I2C)
|
||||
p->interface = PMIC_I2C;
|
||||
p->hw.i2c.addr = CONFIG_SYS_FSL_PMIC_I2C_ADDR;
|
||||
p->hw.i2c.tx_num = 3;
|
||||
p->hw.i2c.tx_num = FSL_PMIC_I2C_LENGTH;
|
||||
p->bus = bus;
|
||||
#else
|
||||
#error "You must select CONFIG_POWER_SPI or CONFIG_PMIC_I2C"
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <serial.h>
|
||||
#include <asm/arch/s3c6400.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
48
drivers/sound/Makefile
Normal file
48
drivers/sound/Makefile
Normal file
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright (C) 2012 Samsung Electronics
|
||||
# R. Chandrasekar <rcsekar@samsung.com>
|
||||
#
|
||||
# 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 $(TOPDIR)/config.mk
|
||||
|
||||
LIB := $(obj)libsound.o
|
||||
|
||||
COBJS-$(CONFIG_SOUND) += sound.o
|
||||
COBJS-$(CONFIG_I2S) += samsung-i2s.o
|
||||
COBJS-$(CONFIG_SOUND_WM8994) += wm8994.o
|
||||
|
||||
COBJS := $(COBJS-y)
|
||||
SRCS := $(COBJS:.o=.c)
|
||||
OBJS := $(addprefix $(obj),$(COBJS))
|
||||
|
||||
all: $(LIB)
|
||||
|
||||
$(LIB): $(obj).depend $(OBJS)
|
||||
$(call cmd_link_o_target, $(OBJS))
|
||||
|
||||
#########################################################################
|
||||
|
||||
# defines $(obj).depend target
|
||||
include $(SRCTREE)/rules.mk
|
||||
|
||||
sinclude $(obj).depend
|
||||
|
||||
#
|
||||
358
drivers/sound/samsung-i2s.c
Normal file
358
drivers/sound/samsung-i2s.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* R. Chandrasekar <rcsekar@samsung.com>
|
||||
*
|
||||
* 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 <asm/arch/clk.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#include <asm/arch/i2s-regs.h>
|
||||
#include <asm/io.h>
|
||||
#include <common.h>
|
||||
#include <sound.h>
|
||||
#include <i2s.h>
|
||||
|
||||
#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
|
||||
#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
|
||||
#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
|
||||
#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
|
||||
#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
|
||||
|
||||
#define TIMEOUT_I2S_TX 100 /* i2s transfer timeout */
|
||||
|
||||
/*
|
||||
* Sets the frame size for I2S LR clock
|
||||
*
|
||||
* @param i2s_reg i2s regiter address
|
||||
* @param rfs Frame Size
|
||||
*/
|
||||
static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
mod &= ~MOD_RCLK_MASK;
|
||||
|
||||
switch (rfs) {
|
||||
case 768:
|
||||
mod |= MOD_RCLK_768FS;
|
||||
break;
|
||||
case 512:
|
||||
mod |= MOD_RCLK_512FS;
|
||||
break;
|
||||
case 384:
|
||||
mod |= MOD_RCLK_384FS;
|
||||
break;
|
||||
default:
|
||||
mod |= MOD_RCLK_256FS;
|
||||
break;
|
||||
}
|
||||
|
||||
writel(mod, &i2s_reg->mod);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the i2s transfer control
|
||||
*
|
||||
* @param i2s_reg i2s regiter address
|
||||
* @param on 1 enable tx , 0 disable tx transfer
|
||||
*/
|
||||
static void i2s_txctrl(struct i2s_reg *i2s_reg, int on)
|
||||
{
|
||||
unsigned int con = readl(&i2s_reg->con);
|
||||
unsigned int mod = readl(&i2s_reg->mod) & ~MOD_MASK;
|
||||
|
||||
if (on) {
|
||||
con |= CON_ACTIVE;
|
||||
con &= ~CON_TXCH_PAUSE;
|
||||
|
||||
} else {
|
||||
|
||||
con |= CON_TXCH_PAUSE;
|
||||
con &= ~CON_ACTIVE;
|
||||
}
|
||||
|
||||
writel(mod, &i2s_reg->mod);
|
||||
writel(con, &i2s_reg->con);
|
||||
}
|
||||
|
||||
/*
|
||||
* set the bit clock frame size (in multiples of LRCLK)
|
||||
*
|
||||
* @param i2s_reg i2s regiter address
|
||||
* @param bfs bit Frame Size
|
||||
*/
|
||||
static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
mod &= ~MOD_BCLK_MASK;
|
||||
|
||||
switch (bfs) {
|
||||
case 48:
|
||||
mod |= MOD_BCLK_48FS;
|
||||
break;
|
||||
case 32:
|
||||
mod |= MOD_BCLK_32FS;
|
||||
break;
|
||||
case 24:
|
||||
mod |= MOD_BCLK_24FS;
|
||||
break;
|
||||
case 16:
|
||||
mod |= MOD_BCLK_16FS;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
writel(mod, &i2s_reg->mod);
|
||||
}
|
||||
|
||||
/*
|
||||
* flushes the i2stx fifo
|
||||
*
|
||||
* @param i2s_reg i2s regiter 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)
|
||||
{
|
||||
/* Flush the FIFO */
|
||||
setbits_le32(&i2s_reg->fic, flush);
|
||||
clrbits_le32(&i2s_reg->fic, flush);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set System Clock direction
|
||||
*
|
||||
* @param i2s_reg i2s regiter 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)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
if (dir == SND_SOC_CLOCK_IN)
|
||||
mod |= MOD_CDCLKCON;
|
||||
else
|
||||
mod &= ~MOD_CDCLKCON;
|
||||
|
||||
writel(mod, &i2s_reg->mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets I2S Clcok format
|
||||
*
|
||||
* @param fmt i2s clock properties
|
||||
* @param i2s_reg i2s regiter address
|
||||
*
|
||||
* @return int value 0 for success, -1 in case of error
|
||||
*/
|
||||
int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
unsigned int tmp = 0;
|
||||
unsigned int ret = 0;
|
||||
|
||||
/* Format is priority */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
tmp |= MOD_LR_RLOW;
|
||||
tmp |= MOD_SDF_MSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
tmp |= MOD_LR_RLOW;
|
||||
tmp |= MOD_SDF_LSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
tmp |= MOD_SDF_IIS;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid format priority [0x%x]\n", __func__,
|
||||
(fmt & SND_SOC_DAIFMT_FORMAT_MASK));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* INV flag is relative to the FORMAT flag - if set it simply
|
||||
* flips the polarity specified by the Standard
|
||||
*/
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
if (tmp & MOD_LR_RLOW)
|
||||
tmp &= ~MOD_LR_RLOW;
|
||||
else
|
||||
tmp |= MOD_LR_RLOW;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid clock ploarity input [0x%x]\n", __func__,
|
||||
(fmt & SND_SOC_DAIFMT_INV_MASK));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
tmp |= MOD_SLAVE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* Set default source clock in Master mode */
|
||||
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;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid master selection [0x%x]\n", __func__,
|
||||
(fmt & SND_SOC_DAIFMT_MASTER_MASK));
|
||||
return -1;
|
||||
}
|
||||
|
||||
mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE);
|
||||
mod |= tmp;
|
||||
writel(mod, &i2s_reg->mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the sample width in bits
|
||||
*
|
||||
* @param blc samplewidth (size of sample in bits)
|
||||
* @param i2s_reg i2s regiter address
|
||||
*
|
||||
* @return int value 0 for success, -1 in case of error
|
||||
*/
|
||||
int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc)
|
||||
{
|
||||
unsigned int mod = readl(&i2s_reg->mod);
|
||||
|
||||
mod &= ~MOD_BLCP_MASK;
|
||||
mod &= ~MOD_BLC_MASK;
|
||||
|
||||
switch (blc) {
|
||||
case 8:
|
||||
mod |= MOD_BLCP_8BIT;
|
||||
mod |= MOD_BLC_8BIT;
|
||||
break;
|
||||
case 16:
|
||||
mod |= MOD_BLCP_16BIT;
|
||||
mod |= MOD_BLC_16BIT;
|
||||
break;
|
||||
case 24:
|
||||
mod |= MOD_BLCP_24BIT;
|
||||
mod |= MOD_BLC_24BIT;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid sample size input [0x%x]\n",
|
||||
__func__, blc);
|
||||
return -1;
|
||||
}
|
||||
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 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 */
|
||||
}
|
||||
|
||||
/* fill the tx buffer before stating the tx transmit */
|
||||
for (i = 0; i < FIFO_LENGTH; i++)
|
||||
writel(*data++, &i2s_reg->txd);
|
||||
|
||||
data_size -= 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--;
|
||||
} else {
|
||||
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;
|
||||
|
||||
/* Initialize GPIO for I2s */
|
||||
exynos_pinmux_config(PERIPH_ID_I2S1, 0);
|
||||
|
||||
/* 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 Audio1 */
|
||||
set_i2s_clk_source();
|
||||
|
||||
/* Set Prescaler to get MCLK */
|
||||
set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk,
|
||||
(pi2s_tx->samplingrate * (pi2s_tx->rfs)));
|
||||
|
||||
/* Configure I2s format */
|
||||
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;
|
||||
}
|
||||
|
||||
i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs);
|
||||
/* disable i2s transfer flag and flush the fifo */
|
||||
i2s_txctrl(i2s_reg, I2S_TX_OFF);
|
||||
i2s_fifo(i2s_reg, FIC_TXFLUSH);
|
||||
} else {
|
||||
debug("%s: failed\n", __func__);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
228
drivers/sound/sound.c
Normal file
228
drivers/sound/sound.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* R. Chandrasekar <rcsekar@samsung.com>
|
||||
*
|
||||
* 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 <malloc.h>
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include <i2s.h>
|
||||
#include <sound.h>
|
||||
#include "wm8994.h"
|
||||
#include <asm/arch/sound.h>
|
||||
|
||||
/* defines */
|
||||
#define SOUND_400_HZ 400
|
||||
#define SOUND_BITS_IN_BYTE 8
|
||||
|
||||
static struct i2stx_info g_i2stx_pri;
|
||||
static struct sound_codec_info g_codec_info;
|
||||
|
||||
/*
|
||||
* get_sound_fdt_values gets fdt values for i2s parameters
|
||||
*
|
||||
* @param i2stx_info i2s transmitter transfer param structure
|
||||
* @param blob FDT blob
|
||||
*/
|
||||
static void get_sound_i2s_values(struct i2stx_info *i2s)
|
||||
{
|
||||
i2s->base_address = samsung_get_base_i2s();
|
||||
i2s->audio_pll_clk = I2S_PLL_CLK;
|
||||
i2s->samplingrate = I2S_SAMPLING_RATE;
|
||||
i2s->bitspersample = I2S_BITS_PER_SAMPLE;
|
||||
i2s->channels = I2S_CHANNELS;
|
||||
i2s->rfs = I2S_RFS;
|
||||
i2s->bfs = I2S_BFS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets fdt values for wm8994 config parameters
|
||||
*
|
||||
* @param pcodec_info codec information structure
|
||||
* @param blob FDT blob
|
||||
* @return int value, 0 for success
|
||||
*/
|
||||
static int get_sound_wm8994_values(struct sound_codec_info *pcodec_info)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
switch (AUDIO_COMPAT) {
|
||||
case AUDIO_COMPAT_SPI:
|
||||
debug("%s: Support not added for SPI interface\n", __func__);
|
||||
return -1;
|
||||
break;
|
||||
case AUDIO_COMPAT_I2C:
|
||||
pcodec_info->i2c_bus = AUDIO_I2C_BUS;
|
||||
pcodec_info->i2c_dev_addr = AUDIO_I2C_REG;
|
||||
debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr);
|
||||
break;
|
||||
default:
|
||||
debug("%s: Unknown compat id %d\n", __func__, AUDIO_COMPAT);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (error == -1) {
|
||||
debug("fail to get wm8994 codec node properties\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets fdt values for codec config parameters
|
||||
*
|
||||
* @param pcodec_info codec information structure
|
||||
* @param blob FDT blob
|
||||
* @return int value, 0 for success
|
||||
*/
|
||||
static int get_sound_codec_values(struct sound_codec_info *pcodec_info)
|
||||
{
|
||||
int error = 0;
|
||||
const char *codectype;
|
||||
|
||||
codectype = AUDIO_CODEC;
|
||||
|
||||
if (!strcmp(codectype, "wm8994")) {
|
||||
pcodec_info->codec_type = CODEC_WM_8994;
|
||||
error = get_sound_wm8994_values(pcodec_info);
|
||||
} else {
|
||||
error = -1;
|
||||
}
|
||||
|
||||
if (error == -1) {
|
||||
debug("fail to get sound codec node properties\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sound_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct i2stx_info *pi2s_tx = &g_i2stx_pri;
|
||||
struct sound_codec_info *pcodec_info = &g_codec_info;
|
||||
|
||||
/* Get the I2S Values */
|
||||
get_sound_i2s_values(pi2s_tx);
|
||||
|
||||
/* Get the codec Values */
|
||||
if (get_sound_codec_values(pcodec_info) < 0)
|
||||
return -1;
|
||||
|
||||
ret = i2s_tx_init(pi2s_tx);
|
||||
if (ret) {
|
||||
debug("%s: Failed to init i2c transmit: ret=%d\n", __func__,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check the codec type and initialise the same */
|
||||
if (pcodec_info->codec_type == CODEC_WM_8994) {
|
||||
ret = wm8994_init(pcodec_info, WM8994_AIF2,
|
||||
pi2s_tx->samplingrate,
|
||||
(pi2s_tx->samplingrate * (pi2s_tx->rfs)),
|
||||
pi2s_tx->bitspersample, pi2s_tx->channels);
|
||||
} else {
|
||||
debug("%s: Unknown code type %d\n", __func__,
|
||||
pcodec_info->codec_type);
|
||||
return -1;
|
||||
}
|
||||
if (ret) {
|
||||
debug("%s: Codec init failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates square wave sound data for 1 second
|
||||
*
|
||||
* @param data data buffer pointer
|
||||
* @param size size of the buffer
|
||||
* @param freq frequency of the wave
|
||||
*/
|
||||
static void sound_prepare_buffer(unsigned short *data, int size, uint32_t freq)
|
||||
{
|
||||
const int sample = 48000;
|
||||
const unsigned short amplitude = 16000; /* between 1 and 32767 */
|
||||
const int period = freq ? sample / freq : 0;
|
||||
const int half = period / 2;
|
||||
|
||||
assert(freq);
|
||||
|
||||
/* Make sure we don't overflow our buffer */
|
||||
if (size % 2)
|
||||
size--;
|
||||
|
||||
while (size) {
|
||||
int i;
|
||||
for (i = 0; size && i < half; i++) {
|
||||
size -= 2;
|
||||
*data++ = amplitude;
|
||||
*data++ = amplitude;
|
||||
}
|
||||
for (i = 0; size && i < period - half; i++) {
|
||||
size -= 2;
|
||||
*data++ = -amplitude;
|
||||
*data++ = -amplitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sound_play(uint32_t msec, uint32_t frequency)
|
||||
{
|
||||
unsigned int *data;
|
||||
unsigned long data_size;
|
||||
unsigned int ret = 0;
|
||||
|
||||
/*Buffer length computation */
|
||||
data_size = g_i2stx_pri.samplingrate * g_i2stx_pri.channels;
|
||||
data_size *= (g_i2stx_pri.bitspersample / SOUND_BITS_IN_BYTE);
|
||||
data = malloc(data_size);
|
||||
|
||||
if (data == NULL) {
|
||||
debug("%s: malloc failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sound_prepare_buffer((unsigned short *)data,
|
||||
data_size / sizeof(unsigned short), frequency);
|
||||
|
||||
while (msec >= 1000) {
|
||||
ret = i2s_transfer_tx_data(&g_i2stx_pri, data,
|
||||
(data_size / sizeof(int)));
|
||||
msec -= 1000;
|
||||
}
|
||||
if (msec) {
|
||||
unsigned long size =
|
||||
(data_size * msec) / (sizeof(int) * 1000);
|
||||
|
||||
ret = i2s_transfer_tx_data(&g_i2stx_pri, data, size);
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
792
drivers/sound/wm8994.c
Normal file
792
drivers/sound/wm8994.c
Normal file
@@ -0,0 +1,792 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* R. Chandrasekar <rcsekar@samsung.com>
|
||||
*
|
||||
* 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 <asm/arch/clk.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include <common.h>
|
||||
#include <div64.h>
|
||||
#include <i2c.h>
|
||||
#include <i2s.h>
|
||||
#include <sound.h>
|
||||
#include "wm8994.h"
|
||||
#include "wm8994_registers.h"
|
||||
|
||||
/* defines for wm8994 system clock selection */
|
||||
#define SEL_MCLK1 0x00
|
||||
#define SEL_MCLK2 0x08
|
||||
#define SEL_FLL1 0x10
|
||||
#define SEL_FLL2 0x18
|
||||
|
||||
/* fll config to configure fll */
|
||||
struct wm8994_fll_config {
|
||||
int src; /* Source */
|
||||
int in; /* Input frequency in Hz */
|
||||
int out; /* output frequency in Hz */
|
||||
};
|
||||
|
||||
/* codec private data */
|
||||
struct wm8994_priv {
|
||||
enum wm8994_type type; /* codec type of wolfson */
|
||||
int revision; /* Revision */
|
||||
int sysclk[WM8994_MAX_AIF]; /* System clock frequency in Hz */
|
||||
int mclk[WM8994_MAX_AIF]; /* master clock frequency in Hz */
|
||||
int aifclk[WM8994_MAX_AIF]; /* audio interface clock in Hz */
|
||||
struct wm8994_fll_config fll[2]; /* fll config to configure fll */
|
||||
};
|
||||
|
||||
/* wm 8994 supported sampling rate values */
|
||||
static unsigned int src_rate[] = {
|
||||
8000, 11025, 12000, 16000, 22050, 24000,
|
||||
32000, 44100, 48000, 88200, 96000
|
||||
};
|
||||
|
||||
/* op clock divisions */
|
||||
static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
|
||||
|
||||
/* lr clock frame size ratio */
|
||||
static int fs_ratios[] = {
|
||||
64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536
|
||||
};
|
||||
|
||||
/* bit clock divisors */
|
||||
static int bclk_divs[] = {
|
||||
10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480,
|
||||
640, 880, 960, 1280, 1760, 1920
|
||||
};
|
||||
|
||||
static struct wm8994_priv g_wm8994_info;
|
||||
static unsigned char g_wm8994_i2c_dev_addr;
|
||||
|
||||
/*
|
||||
* Initialise I2C for wm 8994
|
||||
*
|
||||
* @param bus no i2c bus number in which wm8994 is connected
|
||||
*/
|
||||
static void wm8994_i2c_init(int bus_no)
|
||||
{
|
||||
i2c_set_bus_num(bus_no);
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes value to a device register through i2c
|
||||
*
|
||||
* @param reg reg number to be write
|
||||
* @param data data to be writen to the above registor
|
||||
*
|
||||
* @return int value 1 for change, 0 for no change or negative error code.
|
||||
*/
|
||||
static int wm8994_i2c_write(unsigned int reg, unsigned short data)
|
||||
{
|
||||
unsigned char val[2];
|
||||
|
||||
val[0] = (unsigned char)((data >> 8) & 0xff);
|
||||
val[1] = (unsigned char)(data & 0xff);
|
||||
debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data);
|
||||
|
||||
return i2c_write(g_wm8994_i2c_dev_addr, reg, 2, val, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a value from a device register through i2c
|
||||
*
|
||||
* @param reg reg number to be read
|
||||
* @param data address of read data to be stored
|
||||
*
|
||||
* @return int value 0 for success, -1 in case of error.
|
||||
*/
|
||||
static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data)
|
||||
{
|
||||
unsigned char val[2];
|
||||
int ret;
|
||||
|
||||
ret = i2c_read(g_wm8994_i2c_dev_addr, reg, 2, val, 2);
|
||||
if (ret != 0) {
|
||||
debug("%s: Error while reading register %#04x\n",
|
||||
__func__, reg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*data = val[0];
|
||||
*data <<= 8;
|
||||
*data |= val[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* update device register bits through i2c
|
||||
*
|
||||
* @param reg codec register
|
||||
* @param mask register mask
|
||||
* @param value new value
|
||||
*
|
||||
* @return int value 1 if change in the register value,
|
||||
* 0 for no change or negative error code.
|
||||
*/
|
||||
static int wm8994_update_bits(unsigned int reg, unsigned short mask,
|
||||
unsigned short value)
|
||||
{
|
||||
int change , ret = 0;
|
||||
unsigned short old, new;
|
||||
|
||||
if (wm8994_i2c_read(reg, &old) != 0)
|
||||
return -1;
|
||||
new = (old & ~mask) | (value & mask);
|
||||
change = (old != new) ? 1 : 0;
|
||||
if (change)
|
||||
ret = wm8994_i2c_write(reg, new);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets i2s set format
|
||||
*
|
||||
* @param aif_id Interface ID
|
||||
* @param fmt i2S format
|
||||
*
|
||||
* @return -1 for error and 0 Success.
|
||||
*/
|
||||
int wm8994_set_fmt(int aif_id, unsigned int fmt)
|
||||
{
|
||||
int ms_reg;
|
||||
int aif_reg;
|
||||
int ms = 0;
|
||||
int aif = 0;
|
||||
int aif_clk = 0;
|
||||
int error = 0;
|
||||
|
||||
switch (aif_id) {
|
||||
case 1:
|
||||
ms_reg = WM8994_AIF1_MASTER_SLAVE;
|
||||
aif_reg = WM8994_AIF1_CONTROL_1;
|
||||
aif_clk = WM8994_AIF1_CLOCKING_1;
|
||||
break;
|
||||
case 2:
|
||||
ms_reg = WM8994_AIF2_MASTER_SLAVE;
|
||||
aif_reg = WM8994_AIF2_CONTROL_1;
|
||||
aif_clk = WM8994_AIF2_CLOCKING_1;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid audio interface selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
ms = WM8994_AIF1_MSTR;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s master selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
aif |= WM8994_AIF1_LRCLK_INV;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
aif |= 0x18;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
aif |= 0x10;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
aif |= 0x8;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s format selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
/* frame inversion not valid for DSP modes */
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
aif |= WM8994_AIF1_BCLK_INV;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s frame inverse selection\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
aif |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
aif |= WM8994_AIF1_BCLK_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
aif |= WM8994_AIF1_LRCLK_INV;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s clock polarity selection\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug("%s: Invalid i2s format selection\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = wm8994_update_bits(aif_reg, WM8994_AIF1_BCLK_INV |
|
||||
WM8994_AIF1_LRCLK_INV_MASK | WM8994_AIF1_FMT_MASK, aif);
|
||||
|
||||
error |= wm8994_update_bits(ms_reg, WM8994_AIF1_MSTR_MASK, ms);
|
||||
error |= wm8994_update_bits(aif_clk, WM8994_AIF1CLK_ENA_MASK,
|
||||
WM8994_AIF1CLK_ENA);
|
||||
if (error < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets hw params FOR WM8994
|
||||
*
|
||||
* @param wm8994 wm8994 information pointer
|
||||
* @param aif_id Audio interface ID
|
||||
* @param sampling_rate Sampling rate
|
||||
* @param bits_per_sample Bits per sample
|
||||
* @param Channels Channels in the given audio input
|
||||
*
|
||||
* @return -1 for error and 0 Success.
|
||||
*/
|
||||
static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id,
|
||||
unsigned int sampling_rate, unsigned int bits_per_sample,
|
||||
unsigned int channels)
|
||||
{
|
||||
int aif1_reg;
|
||||
int aif2_reg;
|
||||
int bclk_reg;
|
||||
int bclk = 0;
|
||||
int rate_reg;
|
||||
int aif1 = 0;
|
||||
int aif2 = 0;
|
||||
int rate_val = 0;
|
||||
int id = aif_id - 1;
|
||||
int i, cur_val, best_val, bclk_rate, best;
|
||||
unsigned short reg_data;
|
||||
int ret = 0;
|
||||
|
||||
switch (aif_id) {
|
||||
case 1:
|
||||
aif1_reg = WM8994_AIF1_CONTROL_1;
|
||||
aif2_reg = WM8994_AIF1_CONTROL_2;
|
||||
bclk_reg = WM8994_AIF1_BCLK;
|
||||
rate_reg = WM8994_AIF1_RATE;
|
||||
break;
|
||||
case 2:
|
||||
aif1_reg = WM8994_AIF2_CONTROL_1;
|
||||
aif2_reg = WM8994_AIF2_CONTROL_2;
|
||||
bclk_reg = WM8994_AIF2_BCLK;
|
||||
rate_reg = WM8994_AIF2_RATE;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
bclk_rate = sampling_rate * 32;
|
||||
switch (bits_per_sample) {
|
||||
case 16:
|
||||
bclk_rate *= 16;
|
||||
break;
|
||||
case 20:
|
||||
bclk_rate *= 20;
|
||||
aif1 |= 0x20;
|
||||
break;
|
||||
case 24:
|
||||
bclk_rate *= 24;
|
||||
aif1 |= 0x40;
|
||||
break;
|
||||
case 32:
|
||||
bclk_rate *= 32;
|
||||
aif1 |= 0x60;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Try to find an appropriate sample rate; look for an exact match. */
|
||||
for (i = 0; i < ARRAY_SIZE(src_rate); i++)
|
||||
if (src_rate[i] == sampling_rate)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(src_rate)) {
|
||||
debug("%s: Could not get the best matching samplingrate\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rate_val |= i << WM8994_AIF1_SR_SHIFT;
|
||||
|
||||
/* AIFCLK/fs ratio; look for a close match in either direction */
|
||||
best = 0;
|
||||
best_val = abs((fs_ratios[0] * sampling_rate)
|
||||
- wm8994->aifclk[id]);
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) {
|
||||
cur_val = abs((fs_ratios[i] * sampling_rate)
|
||||
- wm8994->aifclk[id]);
|
||||
if (cur_val >= best_val)
|
||||
continue;
|
||||
best = i;
|
||||
best_val = cur_val;
|
||||
}
|
||||
|
||||
rate_val |= best;
|
||||
|
||||
/*
|
||||
* We may not get quite the right frequency if using
|
||||
* approximate clocks so look for the closest match that is
|
||||
* higher than the target (we need to ensure that there enough
|
||||
* BCLKs to clock out the samples).
|
||||
*/
|
||||
best = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
|
||||
cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate;
|
||||
if (cur_val < 0) /* BCLK table is sorted */
|
||||
break;
|
||||
best = i;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(bclk_divs)) {
|
||||
debug("%s: Could not get the best matching bclk division\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best];
|
||||
bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT;
|
||||
|
||||
if (wm8994_i2c_read(aif1_reg, ®_data) != 0) {
|
||||
debug("%s: AIF1 register read Failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((channels == 1) && ((reg_data & 0x18) == 0x18))
|
||||
aif2 |= WM8994_AIF1_MONO;
|
||||
|
||||
if (wm8994->aifclk[id] == 0) {
|
||||
debug("%s:Audio interface clock not set\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = wm8994_update_bits(aif1_reg, WM8994_AIF1_WL_MASK, aif1);
|
||||
ret |= wm8994_update_bits(aif2_reg, WM8994_AIF1_MONO, aif2);
|
||||
ret |= wm8994_update_bits(bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk);
|
||||
ret |= wm8994_update_bits(rate_reg, WM8994_AIF1_SR_MASK |
|
||||
WM8994_AIF1CLK_RATE_MASK, rate_val);
|
||||
|
||||
debug("rate vale = %x , bclk val= %x\n", rate_val, bclk);
|
||||
|
||||
if (ret < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures Audio interface Clock
|
||||
*
|
||||
* @param wm8994 wm8994 information pointer
|
||||
* @param aif Audio Interface ID
|
||||
*
|
||||
* @return -1 for error and 0 Success.
|
||||
*/
|
||||
static int configure_aif_clock(struct wm8994_priv *wm8994, int aif)
|
||||
{
|
||||
int rate;
|
||||
int reg1 = 0;
|
||||
int offset;
|
||||
int ret;
|
||||
|
||||
/* AIF(1/0) register adress offset calculated */
|
||||
if (aif)
|
||||
offset = 4;
|
||||
else
|
||||
offset = 0;
|
||||
|
||||
switch (wm8994->sysclk[aif]) {
|
||||
case WM8994_SYSCLK_MCLK1:
|
||||
reg1 |= SEL_MCLK1;
|
||||
rate = wm8994->mclk[0];
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_MCLK2:
|
||||
reg1 |= SEL_MCLK2;
|
||||
rate = wm8994->mclk[1];
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_FLL1:
|
||||
reg1 |= SEL_FLL1;
|
||||
rate = wm8994->fll[0].out;
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_FLL2:
|
||||
reg1 |= SEL_FLL2;
|
||||
rate = wm8994->fll[1].out;
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("%s: Invalid input clock selection [%d]\n",
|
||||
__func__, wm8994->sysclk[aif]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if input clock frequenct is more than 135Mhz then divide */
|
||||
if (rate >= WM8994_MAX_INPUT_CLK_FREQ) {
|
||||
rate /= 2;
|
||||
reg1 |= WM8994_AIF1CLK_DIV;
|
||||
}
|
||||
|
||||
wm8994->aifclk[aif] = rate;
|
||||
|
||||
ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset,
|
||||
WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
|
||||
reg1);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_CLOCKING_1,
|
||||
WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK |
|
||||
WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC |
|
||||
WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA);
|
||||
|
||||
if (ret < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures Audio interface for the given frequency
|
||||
*
|
||||
* @param wm8994 wm8994 information
|
||||
* @param aif_id Audio Interface
|
||||
* @param clk_id Input Clock ID
|
||||
* @param freq Sampling frequency in Hz
|
||||
*
|
||||
* @return -1 for error and 0 success.
|
||||
*/
|
||||
static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id,
|
||||
int clk_id, unsigned int freq)
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
wm8994->sysclk[aif_id - 1] = clk_id;
|
||||
|
||||
switch (clk_id) {
|
||||
case WM8994_SYSCLK_MCLK1:
|
||||
wm8994->mclk[0] = freq;
|
||||
if (aif_id == 2) {
|
||||
ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_2 ,
|
||||
WM8994_AIF2DAC_DIV_MASK , 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_MCLK2:
|
||||
/* TODO: Set GPIO AF */
|
||||
wm8994->mclk[1] = freq;
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_FLL1:
|
||||
case WM8994_SYSCLK_FLL2:
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_OPCLK:
|
||||
/*
|
||||
* Special case - a division (times 10) is given and
|
||||
* no effect on main clocking.
|
||||
*/
|
||||
if (freq) {
|
||||
for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
|
||||
if (opclk_divs[i] == freq)
|
||||
break;
|
||||
if (i == ARRAY_SIZE(opclk_divs)) {
|
||||
debug("%s frequency divisor not found\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
ret = wm8994_update_bits(WM8994_CLOCKING_2,
|
||||
WM8994_OPCLK_DIV_MASK, i);
|
||||
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2,
|
||||
WM8994_OPCLK_ENA, WM8994_OPCLK_ENA);
|
||||
} else {
|
||||
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2,
|
||||
WM8994_OPCLK_ENA, 0);
|
||||
}
|
||||
|
||||
default:
|
||||
debug("%s Invalid input clock selection [%d]\n",
|
||||
__func__, clk_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret |= configure_aif_clock(wm8994, aif_id - 1);
|
||||
|
||||
if (ret < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes Volume for AIF2 to HP path
|
||||
*
|
||||
* @returns -1 for error and 0 Success.
|
||||
*
|
||||
*/
|
||||
static int wm8994_init_volume_aif2_dac1(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Unmute AIF2DAC */
|
||||
ret = wm8994_update_bits(WM8994_AIF2_DAC_FILTERS_1,
|
||||
WM8994_AIF2DAC_MUTE_MASK, 0);
|
||||
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_AIF2_DAC_LEFT_VOLUME,
|
||||
WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK,
|
||||
WM8994_AIF2DAC_VU | 0xff);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_AIF2_DAC_RIGHT_VOLUME,
|
||||
WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK,
|
||||
WM8994_AIF2DAC_VU | 0xff);
|
||||
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME,
|
||||
WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK |
|
||||
WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME,
|
||||
WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK |
|
||||
WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0);
|
||||
/* Head Phone Volume */
|
||||
ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D);
|
||||
ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D);
|
||||
|
||||
if (ret < 0) {
|
||||
debug("%s: codec register access error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Intialise wm8994 codec device
|
||||
*
|
||||
* @param wm8994 wm8994 information
|
||||
*
|
||||
* @returns -1 for error and 0 Success.
|
||||
*/
|
||||
static int wm8994_device_init(struct wm8994_priv *wm8994)
|
||||
{
|
||||
const char *devname;
|
||||
unsigned short reg_data;
|
||||
int ret;
|
||||
|
||||
wm8994_i2c_write(WM8994_SOFTWARE_RESET, WM8994_SW_RESET);/* Reset */
|
||||
|
||||
ret = wm8994_i2c_read(WM8994_SOFTWARE_RESET, ®_data);
|
||||
if (ret < 0) {
|
||||
debug("Failed to read ID register\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (reg_data == WM8994_ID) {
|
||||
devname = "WM8994";
|
||||
debug("Device registered as type %d\n", wm8994->type);
|
||||
wm8994->type = WM8994;
|
||||
} else {
|
||||
debug("Device is not a WM8994, ID is %x\n", ret);
|
||||
ret = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wm8994_i2c_read(WM8994_CHIP_REVISION, ®_data);
|
||||
if (ret < 0) {
|
||||
debug("Failed to read revision register: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
wm8994->revision = reg_data;
|
||||
debug("%s revision %c\n", devname, 'A' + wm8994->revision);
|
||||
|
||||
/* VMID Selection */
|
||||
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3);
|
||||
|
||||
/* Charge Pump Enable */
|
||||
ret |= wm8994_update_bits(WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK,
|
||||
WM8994_CP_ENA);
|
||||
|
||||
/* Head Phone Power Enable */
|
||||
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA);
|
||||
|
||||
/* Power enable for AIF2 and DAC1 */
|
||||
ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5,
|
||||
WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK |
|
||||
WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK,
|
||||
WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA |
|
||||
WM8994_DAC1R_ENA);
|
||||
|
||||
/* Head Phone Initialisation */
|
||||
ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1,
|
||||
WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK,
|
||||
WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_DC_SERVO_1,
|
||||
WM8994_DCS_ENA_CHAN_0_MASK |
|
||||
WM8994_DCS_ENA_CHAN_1_MASK , WM8994_DCS_ENA_CHAN_0 |
|
||||
WM8994_DCS_ENA_CHAN_1);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1,
|
||||
WM8994_HPOUT1L_DLY_MASK |
|
||||
WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK |
|
||||
WM8994_HPOUT1R_OUTP_MASK |
|
||||
WM8994_HPOUT1L_RMV_SHORT_MASK |
|
||||
WM8994_HPOUT1R_RMV_SHORT_MASK, WM8994_HPOUT1L_DLY |
|
||||
WM8994_HPOUT1R_DLY | WM8994_HPOUT1L_OUTP |
|
||||
WM8994_HPOUT1R_OUTP | WM8994_HPOUT1L_RMV_SHORT |
|
||||
WM8994_HPOUT1R_RMV_SHORT);
|
||||
|
||||
/* MIXER Config DAC1 to HP */
|
||||
ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_1,
|
||||
WM8994_DAC1L_TO_HPOUT1L_MASK, WM8994_DAC1L_TO_HPOUT1L);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2,
|
||||
WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R);
|
||||
|
||||
/* Routing AIF2 to DAC1 */
|
||||
ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING,
|
||||
WM8994_AIF2DACL_TO_DAC1L_MASK,
|
||||
WM8994_AIF2DACL_TO_DAC1L);
|
||||
|
||||
ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING,
|
||||
WM8994_AIF2DACR_TO_DAC1R_MASK,
|
||||
WM8994_AIF2DACR_TO_DAC1R);
|
||||
|
||||
/* GPIO Settings for AIF2 */
|
||||
/* B CLK */
|
||||
ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK |
|
||||
WM8994_GPIO_FUNCTION_MASK ,
|
||||
WM8994_GPIO_DIR_OUTPUT |
|
||||
WM8994_GPIO_FUNCTION_I2S_CLK);
|
||||
|
||||
/* LR CLK */
|
||||
ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK |
|
||||
WM8994_GPIO_FUNCTION_MASK,
|
||||
WM8994_GPIO_DIR_OUTPUT |
|
||||
WM8994_GPIO_FUNCTION_I2S_CLK);
|
||||
|
||||
/* DATA */
|
||||
ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK |
|
||||
WM8994_GPIO_FUNCTION_MASK,
|
||||
WM8994_GPIO_DIR_OUTPUT |
|
||||
WM8994_GPIO_FUNCTION_I2S_CLK);
|
||||
|
||||
ret |= wm8994_init_volume_aif2_dac1();
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
debug("%s: Codec chip init ok\n", __func__);
|
||||
return 0;
|
||||
err:
|
||||
debug("%s: Codec chip init error\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*wm8994 Device Initialisation */
|
||||
int wm8994_init(struct sound_codec_info *pcodec_info,
|
||||
enum en_audio_interface aif_id,
|
||||
int sampling_rate, int mclk_freq,
|
||||
int bits_per_sample, unsigned int channels)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* shift the device address by 1 for 7 bit addressing */
|
||||
g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr;
|
||||
wm8994_i2c_init(pcodec_info->i2c_bus);
|
||||
|
||||
if (pcodec_info->codec_type == CODEC_WM_8994)
|
||||
g_wm8994_info.type = WM8994;
|
||||
else {
|
||||
debug("%s: Codec id [%d] not defined\n", __func__,
|
||||
pcodec_info->codec_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = wm8994_device_init(&g_wm8994_info);
|
||||
if (ret < 0) {
|
||||
debug("%s: wm8994 codec chip init failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1,
|
||||
mclk_freq);
|
||||
if (ret < 0) {
|
||||
debug("%s: wm8994 codec set sys clock failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate,
|
||||
bits_per_sample, channels);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
87
drivers/sound/wm8994.h
Normal file
87
drivers/sound/wm8994.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* R. Chadrasekar <rcsekar@samsung.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef __WM8994_H__
|
||||
#define __WM8994_H__
|
||||
|
||||
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
|
||||
#define WM8994_SYSCLK_MCLK1 1
|
||||
#define WM8994_SYSCLK_MCLK2 2
|
||||
#define WM8994_SYSCLK_FLL1 3
|
||||
#define WM8994_SYSCLK_FLL2 4
|
||||
|
||||
/* Avilable audi interface ports in wm8994 codec */
|
||||
enum en_audio_interface {
|
||||
WM8994_AIF1 = 1,
|
||||
WM8994_AIF2,
|
||||
WM8994_AIF3
|
||||
};
|
||||
|
||||
/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */
|
||||
#define WM8994_SYSCLK_OPCLK 5
|
||||
|
||||
#define WM8994_FLL1 1
|
||||
#define WM8994_FLL2 2
|
||||
|
||||
#define WM8994_FLL_SRC_MCLK1 1
|
||||
#define WM8994_FLL_SRC_MCLK2 2
|
||||
#define WM8994_FLL_SRC_LRCLK 3
|
||||
#define WM8994_FLL_SRC_BCLK 4
|
||||
|
||||
/* maximum available digital interfac in the dac to configure */
|
||||
#define WM8994_MAX_AIF 2
|
||||
|
||||
#define WM8994_MAX_INPUT_CLK_FREQ 13500000
|
||||
#define WM8994_ID 0x8994
|
||||
|
||||
enum wm8994_vmid_mode {
|
||||
WM8994_VMID_NORMAL,
|
||||
WM8994_VMID_FORCE,
|
||||
};
|
||||
|
||||
/* wm 8994 family devices */
|
||||
enum wm8994_type {
|
||||
WM8994 = 0,
|
||||
WM8958 = 1,
|
||||
WM1811 = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* intialise wm8994 sound codec device for the given configuration
|
||||
*
|
||||
* @param pcodec_info pointer value of the sound codec info structure
|
||||
* parsed from device tree
|
||||
* @param aif_id enum value of codec interface port in which
|
||||
* soc i2s is connected
|
||||
* @param sampling_rate Sampling rate ranges between from 8khz to 96khz
|
||||
* @param mclk_freq Master clock frequency.
|
||||
* @param bits_per_sample bits per Sample can be 16 or 24
|
||||
* @param channels Number of channnels, maximum 2
|
||||
*
|
||||
* @returns -1 for error and 0 Success.
|
||||
*/
|
||||
int wm8994_init(struct sound_codec_info *pcodec_info,
|
||||
enum en_audio_interface aif_id,
|
||||
int sampling_rate, int mclk_freq,
|
||||
int bits_per_sample, unsigned int channels);
|
||||
#endif /*__WM8994_H__ */
|
||||
299
drivers/sound/wm8994_registers.h
Normal file
299
drivers/sound/wm8994_registers.h
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* (C) Copyright 2012 Samsung Electronics
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __WM8994_REGISTERS_H__
|
||||
#define __WM8994_REGISTERS_H__
|
||||
|
||||
/*
|
||||
* Register values.
|
||||
*/
|
||||
#define WM8994_SOFTWARE_RESET 0x00
|
||||
#define WM8994_POWER_MANAGEMENT_1 0x01
|
||||
#define WM8994_POWER_MANAGEMENT_2 0x02
|
||||
#define WM8994_POWER_MANAGEMENT_5 0x05
|
||||
#define WM8994_LEFT_OUTPUT_VOLUME 0x1C
|
||||
#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D
|
||||
#define WM8994_OUTPUT_MIXER_1 0x2D
|
||||
#define WM8994_OUTPUT_MIXER_2 0x2E
|
||||
#define WM8994_CHARGE_PUMP_1 0x4C
|
||||
#define WM8994_DC_SERVO_1 0x54
|
||||
#define WM8994_ANALOGUE_HP_1 0x60
|
||||
#define WM8994_CHIP_REVISION 0x100
|
||||
#define WM8994_AIF1_CLOCKING_1 0x200
|
||||
#define WM8994_AIF1_CLOCKING_2 0x201
|
||||
#define WM8994_AIF2_CLOCKING_1 0x204
|
||||
#define WM8994_CLOCKING_1 0x208
|
||||
#define WM8994_CLOCKING_2 0x209
|
||||
#define WM8994_AIF1_RATE 0x210
|
||||
#define WM8994_AIF2_RATE 0x211
|
||||
#define WM8994_RATE_STATUS 0x212
|
||||
#define WM8994_AIF1_CONTROL_1 0x300
|
||||
#define WM8994_AIF1_CONTROL_2 0x301
|
||||
#define WM8994_AIF1_MASTER_SLAVE 0x302
|
||||
#define WM8994_AIF1_BCLK 0x303
|
||||
#define WM8994_AIF2_CONTROL_1 0x310
|
||||
#define WM8994_AIF2_CONTROL_2 0x311
|
||||
#define WM8994_AIF2_MASTER_SLAVE 0x312
|
||||
#define WM8994_AIF2_BCLK 0x313
|
||||
#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502
|
||||
#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503
|
||||
#define WM8994_AIF2_DAC_FILTERS_1 0x520
|
||||
#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601
|
||||
#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602
|
||||
#define WM8994_DAC1_LEFT_VOLUME 0x610
|
||||
#define WM8994_DAC1_RIGHT_VOLUME 0x611
|
||||
#define WM8994_GPIO_3 0x702
|
||||
#define WM8994_GPIO_4 0x703
|
||||
#define WM8994_GPIO_5 0x704
|
||||
|
||||
/*
|
||||
* Field Definitions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* R0 (0x00) - Software Reset
|
||||
*/
|
||||
/* SW_RESET */
|
||||
#define WM8994_SW_RESET 1
|
||||
/*
|
||||
* R1 (0x01) - Power Management (1)
|
||||
*/
|
||||
/* HPOUT1L_ENA */
|
||||
#define WM8994_HPOUT1L_ENA 0x0200
|
||||
/* HPOUT1L_ENA */
|
||||
#define WM8994_HPOUT1L_ENA_MASK 0x0200
|
||||
/* HPOUT1R_ENA */
|
||||
#define WM8994_HPOUT1R_ENA 0x0100
|
||||
/* HPOUT1R_ENA */
|
||||
#define WM8994_HPOUT1R_ENA_MASK 0x0100
|
||||
/* VMID_SEL - [2:1] */
|
||||
#define WM8994_VMID_SEL_MASK 0x0006
|
||||
/* BIAS_ENA */
|
||||
#define WM8994_BIAS_ENA 0x0001
|
||||
/* BIAS_ENA */
|
||||
#define WM8994_BIAS_ENA_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R2 (0x02) - Power Management (2)
|
||||
*/
|
||||
/* OPCLK_ENA */
|
||||
#define WM8994_OPCLK_ENA 0x0800
|
||||
|
||||
/*
|
||||
* R5 (0x05) - Power Management (5)
|
||||
*/
|
||||
/* AIF2DACL_ENA */
|
||||
#define WM8994_AIF2DACL_ENA 0x2000
|
||||
#define WM8994_AIF2DACL_ENA_MASK 0x2000
|
||||
/* AIF2DACR_ENA */
|
||||
#define WM8994_AIF2DACR_ENA 0x1000
|
||||
#define WM8994_AIF2DACR_ENA_MASK 0x1000
|
||||
/* DAC1L_ENA */
|
||||
#define WM8994_DAC1L_ENA 0x0002
|
||||
#define WM8994_DAC1L_ENA_MASK 0x0002
|
||||
/* DAC1R_ENA */
|
||||
#define WM8994_DAC1R_ENA 0x0001
|
||||
#define WM8994_DAC1R_ENA_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R45 (0x2D) - Output Mixer (1)
|
||||
*/
|
||||
/* DAC1L_TO_HPOUT1L */
|
||||
#define WM8994_DAC1L_TO_HPOUT1L 0x0100
|
||||
#define WM8994_DAC1L_TO_HPOUT1L_MASK 0x0100
|
||||
|
||||
/*
|
||||
* R46 (0x2E) - Output Mixer (2)
|
||||
*/
|
||||
/* DAC1R_TO_HPOUT1R */
|
||||
#define WM8994_DAC1R_TO_HPOUT1R 0x0100
|
||||
#define WM8994_DAC1R_TO_HPOUT1R_MASK 0x0100
|
||||
|
||||
/*
|
||||
* R76 (0x4C) - Charge Pump (1)
|
||||
*/
|
||||
/* CP_ENA */
|
||||
#define WM8994_CP_ENA 0x8000
|
||||
#define WM8994_CP_ENA_MASK 0x8000
|
||||
/*
|
||||
* R84 (0x54) - DC Servo (1)
|
||||
*/
|
||||
/* DCS_ENA_CHAN_1 */
|
||||
#define WM8994_DCS_ENA_CHAN_1 0x0002
|
||||
#define WM8994_DCS_ENA_CHAN_1_MASK 0x0002
|
||||
/* DCS_ENA_CHAN_0 */
|
||||
#define WM8994_DCS_ENA_CHAN_0 0x0001
|
||||
#define WM8994_DCS_ENA_CHAN_0_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R96 (0x60) - Analogue HP (1)
|
||||
*/
|
||||
/* HPOUT1L_RMV_SHORT */
|
||||
#define WM8994_HPOUT1L_RMV_SHORT 0x0080
|
||||
#define WM8994_HPOUT1L_RMV_SHORT_MASK 0x0080
|
||||
/* HPOUT1L_OUTP */
|
||||
#define WM8994_HPOUT1L_OUTP 0x0040
|
||||
#define WM8994_HPOUT1L_OUTP_MASK 0x0040
|
||||
/* HPOUT1L_DLY */
|
||||
#define WM8994_HPOUT1L_DLY 0x0020
|
||||
#define WM8994_HPOUT1L_DLY_MASK 0x0020
|
||||
/* HPOUT1R_RMV_SHORT */
|
||||
#define WM8994_HPOUT1R_RMV_SHORT 0x0008
|
||||
#define WM8994_HPOUT1R_RMV_SHORT_MASK 0x0008
|
||||
/* HPOUT1R_OUTP */
|
||||
#define WM8994_HPOUT1R_OUTP 0x0004
|
||||
#define WM8994_HPOUT1R_OUTP_MASK 0x0004
|
||||
/* HPOUT1R_DLY */
|
||||
#define WM8994_HPOUT1R_DLY 0x0002
|
||||
#define WM8994_HPOUT1R_DLY_MASK 0x0002
|
||||
|
||||
/*
|
||||
* R512 (0x200) - AIF1 Clocking (1)
|
||||
*/
|
||||
/* AIF1CLK_SRC - [4:3] */
|
||||
#define WM8994_AIF1CLK_SRC_MASK 0x0018
|
||||
/* AIF1CLK_DIV */
|
||||
#define WM8994_AIF1CLK_DIV 0x0002
|
||||
/* AIF1CLK_ENA */
|
||||
#define WM8994_AIF1CLK_ENA 0x0001
|
||||
#define WM8994_AIF1CLK_ENA_MASK 0x0001
|
||||
|
||||
/*
|
||||
* R517 (0x205) - AIF2 Clocking (2)
|
||||
*/
|
||||
/* AIF2DAC_DIV - [5:3] */
|
||||
#define WM8994_AIF2DAC_DIV_MASK 0x0038
|
||||
|
||||
/*
|
||||
* R520 (0x208) - Clocking (1)
|
||||
*/
|
||||
/* AIF2DSPCLK_ENA */
|
||||
#define WM8994_AIF2DSPCLK_ENA 0x0004
|
||||
#define WM8994_AIF2DSPCLK_ENA_MASK 0x0004
|
||||
/* SYSDSPCLK_ENA */
|
||||
#define WM8994_SYSDSPCLK_ENA 0x0002
|
||||
#define WM8994_SYSDSPCLK_ENA_MASK 0x0002
|
||||
/* SYSCLK_SRC */
|
||||
#define WM8994_SYSCLK_SRC 0x0001
|
||||
|
||||
/*
|
||||
* R521 (0x209) - Clocking (2)
|
||||
*/
|
||||
/* OPCLK_DIV - [2:0] */
|
||||
#define WM8994_OPCLK_DIV_MASK 0x0007
|
||||
|
||||
/*
|
||||
* R528 (0x210) - AIF1 Rate
|
||||
*/
|
||||
/* AIF1_SR - [7:4] */
|
||||
#define WM8994_AIF1_SR_MASK 0x00F0
|
||||
#define WM8994_AIF1_SR_SHIFT 4
|
||||
/* AIF1CLK_RATE - [3:0] */
|
||||
#define WM8994_AIF1CLK_RATE_MASK 0x000F
|
||||
|
||||
/*
|
||||
* R768 (0x300) - AIF1 Control (1)
|
||||
*/
|
||||
/* AIF1_BCLK_INV */
|
||||
#define WM8994_AIF1_BCLK_INV 0x0100
|
||||
/* AIF1_LRCLK_INV */
|
||||
#define WM8994_AIF1_LRCLK_INV 0x0080
|
||||
#define WM8994_AIF1_LRCLK_INV_MASK 0x0080
|
||||
/* AIF1_WL - [6:5] */
|
||||
#define WM8994_AIF1_WL_MASK 0x0060
|
||||
/* AIF1_FMT - [4:3] */
|
||||
#define WM8994_AIF1_FMT_MASK 0x0018
|
||||
|
||||
/*
|
||||
* R769 (0x301) - AIF1 Control (2)
|
||||
*/
|
||||
/* AIF1_MONO */
|
||||
#define WM8994_AIF1_MONO 0x0100
|
||||
|
||||
/*
|
||||
* R770 (0x302) - AIF1 Master/Slave
|
||||
*/
|
||||
/* AIF1_MSTR */
|
||||
#define WM8994_AIF1_MSTR 0x4000
|
||||
#define WM8994_AIF1_MSTR_MASK 0x4000
|
||||
|
||||
/*
|
||||
* R771 (0x303) - AIF1 BCLK
|
||||
*/
|
||||
/* AIF1_BCLK_DIV - [8:4] */
|
||||
#define WM8994_AIF1_BCLK_DIV_MASK 0x01F0
|
||||
#define WM8994_AIF1_BCLK_DIV_SHIFT 4
|
||||
|
||||
/*
|
||||
* R1282 (0x502) - AIF2 DAC Left Volume
|
||||
*/
|
||||
/* AIF2DAC_VU */
|
||||
#define WM8994_AIF2DAC_VU 0x0100
|
||||
#define WM8994_AIF2DAC_VU_MASK 0x0100
|
||||
/* AIF2DACL_VOL - [7:0] */
|
||||
#define WM8994_AIF2DACL_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R1283 (0x503) - AIF2 DAC Right Volume
|
||||
*/
|
||||
/* AIF2DACR_VOL - [7:0] */
|
||||
#define WM8994_AIF2DACR_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R1312 (0x520) - AIF2 DAC Filters (1)
|
||||
*/
|
||||
/* AIF2DAC_MUTE */
|
||||
#define WM8994_AIF2DAC_MUTE_MASK 0x0200
|
||||
|
||||
/*
|
||||
* R1537 (0x601) - DAC1 Left Mixer Routing
|
||||
*/
|
||||
/* AIF2DACL_TO_DAC1L */
|
||||
#define WM8994_AIF2DACL_TO_DAC1L 0x0004
|
||||
#define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004
|
||||
|
||||
/*
|
||||
* R1538 (0x602) - DAC1 Right Mixer Routing
|
||||
*/
|
||||
/* AIF2DACR_TO_DAC1R */
|
||||
#define WM8994_AIF2DACR_TO_DAC1R 0x0004
|
||||
#define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004
|
||||
|
||||
/*
|
||||
* R1552 (0x610) - DAC1 Left Volume
|
||||
*/
|
||||
/* DAC1L_MUTE */
|
||||
#define WM8994_DAC1L_MUTE_MASK 0x0200
|
||||
/* DAC1_VU */
|
||||
#define WM8994_DAC1_VU 0x0100
|
||||
#define WM8994_DAC1_VU_MASK 0x0100
|
||||
/* DAC1L_VOL - [7:0] */
|
||||
#define WM8994_DAC1L_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* R1553 (0x611) - DAC1 Right Volume
|
||||
*/
|
||||
/* DAC1R_MUTE */
|
||||
#define WM8994_DAC1R_MUTE_MASK 0x0200
|
||||
/* DAC1R_VOL - [7:0] */
|
||||
#define WM8994_DAC1R_VOL_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* GPIO
|
||||
*/
|
||||
/* OUTPUT PIN */
|
||||
#define WM8994_GPIO_DIR_OUTPUT 0x8000
|
||||
/* GPIO PIN MASK */
|
||||
#define WM8994_GPIO_DIR_MASK 0xFFE0
|
||||
/* I2S CLK */
|
||||
#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0000
|
||||
/* GPn FN */
|
||||
#define WM8994_GPIO_FUNCTION_MASK 0x001F
|
||||
#endif
|
||||
@@ -34,6 +34,7 @@ COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o
|
||||
COBJS-$(CONFIG_CF_SPI) += cf_spi.o
|
||||
COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
|
||||
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
|
||||
COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
|
||||
COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
|
||||
COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
|
||||
COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
|
||||
|
||||
367
drivers/spi/exynos_spi.c
Normal file
367
drivers/spi/exynos_spi.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* (C) Copyright 2012 SAMSUNG Electronics
|
||||
* Padmavathi Venna <padma.v@samsung.com>
|
||||
*
|
||||
* 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 <common.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#include <asm/arch-exynos/spi.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* Information about each SPI controller */
|
||||
struct spi_bus {
|
||||
enum periph_id periph_id;
|
||||
s32 frequency; /* Default clock frequency, -1 for none */
|
||||
struct exynos_spi *regs;
|
||||
int inited; /* 1 if this bus is ready for use */
|
||||
};
|
||||
|
||||
/* A list of spi buses that we know about */
|
||||
static struct spi_bus spi_bus[EXYNOS5_SPI_NUM_CONTROLLERS];
|
||||
|
||||
struct exynos_spi_slave {
|
||||
struct spi_slave slave;
|
||||
struct exynos_spi *regs;
|
||||
unsigned int freq; /* Default frequency */
|
||||
unsigned int mode;
|
||||
enum periph_id periph_id; /* Peripheral ID for this device */
|
||||
unsigned int fifo_size;
|
||||
};
|
||||
|
||||
static struct spi_bus *spi_get_bus(unsigned dev_index)
|
||||
{
|
||||
if (dev_index < EXYNOS5_SPI_NUM_CONTROLLERS)
|
||||
return &spi_bus[dev_index];
|
||||
debug("%s: invalid bus %d", __func__, dev_index);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave)
|
||||
{
|
||||
return container_of(slave, struct exynos_spi_slave, slave);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the driver private data
|
||||
*
|
||||
* @param bus ID of the bus that the slave is attached to
|
||||
* @param cs ID of the chip select connected to the slave
|
||||
* @param max_hz Required spi frequency
|
||||
* @param mode Required spi mode (clk polarity, clk phase and
|
||||
* master or slave)
|
||||
* @return new device or NULL
|
||||
*/
|
||||
struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave;
|
||||
struct spi_bus *bus;
|
||||
|
||||
if (!spi_cs_is_valid(busnum, cs)) {
|
||||
debug("%s: Invalid bus/chip select %d, %d\n", __func__,
|
||||
busnum, cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spi_slave = malloc(sizeof(*spi_slave));
|
||||
if (!spi_slave) {
|
||||
debug("%s: Could not allocate spi_slave\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bus = &spi_bus[busnum];
|
||||
spi_slave->slave.bus = busnum;
|
||||
spi_slave->slave.cs = cs;
|
||||
spi_slave->regs = bus->regs;
|
||||
spi_slave->mode = mode;
|
||||
spi_slave->periph_id = bus->periph_id;
|
||||
if (bus->periph_id == PERIPH_ID_SPI1 ||
|
||||
bus->periph_id == PERIPH_ID_SPI2)
|
||||
spi_slave->fifo_size = 64;
|
||||
else
|
||||
spi_slave->fifo_size = 256;
|
||||
|
||||
spi_slave->freq = bus->frequency;
|
||||
if (max_hz)
|
||||
spi_slave->freq = min(max_hz, spi_slave->freq);
|
||||
|
||||
return &spi_slave->slave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free spi controller
|
||||
*
|
||||
* @param slave Pointer to spi_slave to which controller has to
|
||||
* communicate with
|
||||
*/
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
|
||||
free(spi_slave);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush spi tx, rx fifos and reset the SPI controller
|
||||
*
|
||||
* @param slave Pointer to spi_slave to which controller has to
|
||||
* communicate with
|
||||
*/
|
||||
static void spi_flush_fifo(struct spi_slave *slave)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
struct exynos_spi *regs = spi_slave->regs;
|
||||
|
||||
clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the spi base registers, set the required clock frequency and
|
||||
* initialize the gpios
|
||||
*
|
||||
* @param slave Pointer to spi_slave to which controller has to
|
||||
* communicate with
|
||||
* @return zero on success else a negative value
|
||||
*/
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
struct exynos_spi *regs = spi_slave->regs;
|
||||
u32 reg = 0;
|
||||
int ret;
|
||||
|
||||
ret = set_spi_clk(spi_slave->periph_id,
|
||||
spi_slave->freq);
|
||||
if (ret < 0) {
|
||||
debug("%s: Failed to setup spi clock\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE);
|
||||
|
||||
spi_flush_fifo(slave);
|
||||
|
||||
reg = readl(®s->ch_cfg);
|
||||
reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L);
|
||||
|
||||
if (spi_slave->mode & SPI_CPHA)
|
||||
reg |= SPI_CH_CPHA_B;
|
||||
|
||||
if (spi_slave->mode & SPI_CPOL)
|
||||
reg |= SPI_CH_CPOL_L;
|
||||
|
||||
writel(reg, ®s->ch_cfg);
|
||||
writel(SPI_FB_DELAY_180, ®s->fb_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the spi H/W and flush the tx and rx fifos
|
||||
*
|
||||
* @param slave Pointer to spi_slave to which controller has to
|
||||
* communicate with
|
||||
*/
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
spi_flush_fifo(slave);
|
||||
}
|
||||
|
||||
static void spi_get_fifo_levels(struct exynos_spi *regs,
|
||||
int *rx_lvl, int *tx_lvl)
|
||||
{
|
||||
uint32_t spi_sts = readl(®s->spi_sts);
|
||||
|
||||
*rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
|
||||
*tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there's something to transfer, do a software reset and set a
|
||||
* transaction size.
|
||||
*
|
||||
* @param regs SPI peripheral registers
|
||||
* @param count Number of bytes to transfer
|
||||
*/
|
||||
static void spi_request_bytes(struct exynos_spi *regs, int count)
|
||||
{
|
||||
assert(count && count < (1 << 16));
|
||||
setbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt);
|
||||
}
|
||||
|
||||
static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
|
||||
void **dinp, void const **doutp)
|
||||
{
|
||||
struct exynos_spi *regs = spi_slave->regs;
|
||||
uchar *rxp = *dinp;
|
||||
const uchar *txp = *doutp;
|
||||
int rx_lvl, tx_lvl;
|
||||
uint out_bytes, in_bytes;
|
||||
|
||||
out_bytes = in_bytes = todo;
|
||||
|
||||
/*
|
||||
* If there's something to send, do a software reset and set a
|
||||
* transaction size.
|
||||
*/
|
||||
spi_request_bytes(regs, todo);
|
||||
|
||||
/*
|
||||
* Bytes are transmitted/received in pairs. Wait to receive all the
|
||||
* data because then transmission will be done as well.
|
||||
*/
|
||||
while (in_bytes) {
|
||||
int temp;
|
||||
|
||||
/* Keep the fifos full/empty. */
|
||||
spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
|
||||
if (tx_lvl < spi_slave->fifo_size && out_bytes) {
|
||||
temp = txp ? *txp++ : 0xff;
|
||||
writel(temp, ®s->tx_data);
|
||||
out_bytes--;
|
||||
}
|
||||
if (rx_lvl > 0 && in_bytes) {
|
||||
temp = readl(®s->rx_data);
|
||||
if (rxp)
|
||||
*rxp++ = temp;
|
||||
in_bytes--;
|
||||
}
|
||||
}
|
||||
*dinp = rxp;
|
||||
*doutp = txp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer and receive data
|
||||
*
|
||||
* @param slave Pointer to spi_slave to which controller has to
|
||||
* communicate with
|
||||
* @param bitlen No of bits to tranfer or receive
|
||||
* @param dout Pointer to transfer buffer
|
||||
* @param din Pointer to receive buffer
|
||||
* @param flags Flags for transfer begin and end
|
||||
* @return zero on success else a negative value
|
||||
*/
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
void *din, unsigned long flags)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
int upto, todo;
|
||||
int bytelen;
|
||||
|
||||
/* spi core configured to do 8 bit transfers */
|
||||
if (bitlen % 8) {
|
||||
debug("Non byte aligned SPI transfer.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Start the transaction, if necessary. */
|
||||
if ((flags & SPI_XFER_BEGIN))
|
||||
spi_cs_activate(slave);
|
||||
|
||||
/* Exynos SPI limits each transfer to 65535 bytes */
|
||||
bytelen = bitlen / 8;
|
||||
for (upto = 0; upto < bytelen; upto += todo) {
|
||||
todo = min(bytelen - upto, (1 << 16) - 1);
|
||||
spi_rx_tx(spi_slave, todo, &din, &dout);
|
||||
}
|
||||
|
||||
/* Stop the transaction, if necessary. */
|
||||
if ((flags & SPI_XFER_END))
|
||||
spi_cs_deactivate(slave);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the bus and chip select numbers
|
||||
*
|
||||
* @param bus ID of the bus that the slave is attached to
|
||||
* @param cs ID of the chip select connected to the slave
|
||||
* @return one on success else zero
|
||||
*/
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
||||
{
|
||||
return spi_get_bus(bus) && cs == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the CS by driving it LOW
|
||||
*
|
||||
* @param slave Pointer to spi_slave to which controller has to
|
||||
* communicate with
|
||||
*/
|
||||
void spi_cs_activate(struct spi_slave *slave)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
|
||||
clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
|
||||
debug("Activate CS, bus %d\n", spi_slave->slave.bus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the CS by driving it HIGH
|
||||
*
|
||||
* @param slave Pointer to spi_slave to which controller has to
|
||||
* communicate with
|
||||
*/
|
||||
void spi_cs_deactivate(struct spi_slave *slave)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
|
||||
setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
|
||||
debug("Deactivate CS, bus %d\n", spi_slave->slave.bus);
|
||||
}
|
||||
|
||||
static inline struct exynos_spi *get_spi_base(int dev_index)
|
||||
{
|
||||
if (dev_index < 3)
|
||||
return (struct exynos_spi *)samsung_get_base_spi() + dev_index;
|
||||
else
|
||||
return (struct exynos_spi *)samsung_get_base_spi_isp() +
|
||||
(dev_index - 3);
|
||||
}
|
||||
|
||||
/* Sadly there is no error return from this function */
|
||||
void spi_init(void)
|
||||
{
|
||||
int i;
|
||||
struct spi_bus *bus;
|
||||
|
||||
for (i = 0; i < EXYNOS5_SPI_NUM_CONTROLLERS; i++) {
|
||||
bus = &spi_bus[i];
|
||||
bus->regs = get_spi_base(i);
|
||||
bus->periph_id = PERIPH_ID_SPI0 + i;
|
||||
|
||||
/* Although Exynos5 supports upto 50Mhz speed,
|
||||
* we are setting it to 10Mhz for safe side
|
||||
*/
|
||||
bus->frequency = 10000000;
|
||||
bus->inited = 1;
|
||||
}
|
||||
}
|
||||
@@ -140,8 +140,8 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,
|
||||
reg_ctrl = reg_read(®s->ctrl);
|
||||
|
||||
/* Reset spi */
|
||||
reg_write(®s->ctrl, 0);
|
||||
reg_write(®s->ctrl, (reg_ctrl | 0x1));
|
||||
reg_write(®s->ctrl, (reg_ctrl & ~MXC_CSPICTRL_EN));
|
||||
reg_write(®s->ctrl, (reg_ctrl | MXC_CSPICTRL_EN));
|
||||
|
||||
/*
|
||||
* The following computation is taken directly from Freescale's code.
|
||||
@@ -387,7 +387,7 @@ static int decode_cs(struct mxc_spi_slave *mxcs, unsigned int cs)
|
||||
if (cs > 3) {
|
||||
mxcs->gpio = cs >> 8;
|
||||
cs &= 3;
|
||||
ret = gpio_direction_output(mxcs->gpio, 0);
|
||||
ret = gpio_direction_output(mxcs->gpio, !(mxcs->ss_pol));
|
||||
if (ret) {
|
||||
printf("mxc_spi: cannot setup gpio %d\n", mxcs->gpio);
|
||||
return -EINVAL;
|
||||
@@ -414,6 +414,8 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0;
|
||||
|
||||
ret = decode_cs(mxcs, cs);
|
||||
if (ret < 0) {
|
||||
free(mxcs);
|
||||
@@ -425,7 +427,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
mxcs->slave.bus = bus;
|
||||
mxcs->slave.cs = cs;
|
||||
mxcs->base = spi_bases[bus];
|
||||
mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0;
|
||||
|
||||
ret = spi_cfg_mxc(mxcs, cs, max_hz, mode);
|
||||
if (ret) {
|
||||
|
||||
@@ -29,34 +29,63 @@
|
||||
#define MX5_USBOTHER_REGS_OFFSET 0x800
|
||||
|
||||
|
||||
#define MXC_OTG_OFFSET 0
|
||||
#define MXC_H1_OFFSET 0x200
|
||||
#define MXC_H2_OFFSET 0x400
|
||||
#define MXC_OTG_OFFSET 0
|
||||
#define MXC_H1_OFFSET 0x200
|
||||
#define MXC_H2_OFFSET 0x400
|
||||
#define MXC_H3_OFFSET 0x600
|
||||
|
||||
#define MXC_USBCTRL_OFFSET 0
|
||||
#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8
|
||||
#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc
|
||||
#define MXC_USB_CTRL_1_OFFSET 0x10
|
||||
#define MXC_USBH2CTRL_OFFSET 0x14
|
||||
#define MXC_USBH3CTRL_OFFSET 0x18
|
||||
|
||||
/* USB_CTRL */
|
||||
#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG wakeup intr enable */
|
||||
#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) /* OTG power mask */
|
||||
#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* Host1 ULPI interrupt enable */
|
||||
#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 wakeup intr enable */
|
||||
#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* HOST1 power mask */
|
||||
/* OTG wakeup intr enable */
|
||||
#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27)
|
||||
/* OTG power mask */
|
||||
#define MXC_OTG_UCTRL_OPM_BIT (1 << 24)
|
||||
/* OTG power pin polarity */
|
||||
#define MXC_OTG_UCTRL_O_PWR_POL_BIT (1 << 24)
|
||||
/* Host1 ULPI interrupt enable */
|
||||
#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12)
|
||||
/* HOST1 wakeup intr enable */
|
||||
#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11)
|
||||
/* HOST1 power mask */
|
||||
#define MXC_H1_UCTRL_H1PM_BIT (1 << 8)
|
||||
/* HOST1 power pin polarity */
|
||||
#define MXC_H1_UCTRL_H1_PWR_POL_BIT (1 << 8)
|
||||
|
||||
/* USB_PHY_CTRL_FUNC */
|
||||
#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */
|
||||
#define MXC_H1_OC_DIS_BIT (1 << 5) /* UH1 Disable Overcurrent Event */
|
||||
/* OTG Polarity of Overcurrent */
|
||||
#define MXC_OTG_PHYCTRL_OC_POL_BIT (1 << 9)
|
||||
/* OTG Disable Overcurrent Event */
|
||||
#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8)
|
||||
/* UH1 Polarity of Overcurrent */
|
||||
#define MXC_H1_OC_POL_BIT (1 << 6)
|
||||
/* UH1 Disable Overcurrent Event */
|
||||
#define MXC_H1_OC_DIS_BIT (1 << 5)
|
||||
/* OTG Power Pin Polarity */
|
||||
#define MXC_OTG_PHYCTRL_PWR_POL_BIT (1 << 3)
|
||||
|
||||
/* USBH2CTRL */
|
||||
#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8)
|
||||
#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7)
|
||||
#define MXC_H2_UCTRL_H2PM_BIT (1 << 4)
|
||||
#define MXC_H2_UCTRL_H2_OC_POL_BIT (1 << 31)
|
||||
#define MXC_H2_UCTRL_H2_OC_DIS_BIT (1 << 30)
|
||||
#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8)
|
||||
#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7)
|
||||
#define MXC_H2_UCTRL_H2PM_BIT (1 << 4)
|
||||
#define MXC_H2_UCTRL_H2_PWR_POL_BIT (1 << 4)
|
||||
|
||||
/* USBH3CTRL */
|
||||
#define MXC_H3_UCTRL_H3_OC_POL_BIT (1 << 31)
|
||||
#define MXC_H3_UCTRL_H3_OC_DIS_BIT (1 << 30)
|
||||
#define MXC_H3_UCTRL_H3UIE_BIT (1 << 8)
|
||||
#define MXC_H3_UCTRL_H3WIE_BIT (1 << 7)
|
||||
#define MXC_H3_UCTRL_H3_PWR_POL_BIT (1 << 4)
|
||||
|
||||
/* USB_CTRL_1 */
|
||||
#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
|
||||
#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
|
||||
|
||||
/* USB pin configuration */
|
||||
#define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \
|
||||
@@ -143,24 +172,42 @@ int mxc_set_usbcontrol(int port, unsigned int flags)
|
||||
if (flags & MXC_EHCI_INTERNAL_PHY) {
|
||||
v = __raw_readl(usbother_base +
|
||||
MXC_USB_PHY_CTR_FUNC_OFFSET);
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
/* OC/USBPWR is not used */
|
||||
v |= MXC_OTG_PHYCTRL_OC_DIS_BIT;
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_OTG_PHYCTRL_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_OTG_PHYCTRL_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
/* OC/USBPWR is used */
|
||||
v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT;
|
||||
else
|
||||
/* OC/USBPWR is not used */
|
||||
v |= MXC_OTG_PHYCTRL_OC_DIS_BIT;
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_OTG_PHYCTRL_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_OTG_PHYCTRL_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base +
|
||||
MXC_USB_PHY_CTR_FUNC_OFFSET);
|
||||
|
||||
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v |= MXC_OTG_UCTRL_OPM_BIT;
|
||||
else
|
||||
v &= ~MXC_OTG_UCTRL_OPM_BIT;
|
||||
else
|
||||
v |= MXC_OTG_UCTRL_OPM_BIT;
|
||||
#endif
|
||||
#ifdef CONFIG_MX53
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_OTG_UCTRL_O_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_OTG_UCTRL_O_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
|
||||
}
|
||||
break;
|
||||
case 1: /* Host 1 Host ULPI */
|
||||
case 1: /* Host 1 ULPI */
|
||||
#ifdef CONFIG_MX51
|
||||
/* The clock for the USBH1 ULPI port will come externally
|
||||
from the PHY. */
|
||||
@@ -170,13 +217,25 @@ int mxc_set_usbcontrol(int port, unsigned int flags)
|
||||
#endif
|
||||
|
||||
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */
|
||||
v &= ~MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask unused */
|
||||
else
|
||||
v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */
|
||||
v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */
|
||||
#endif
|
||||
#ifdef CONFIG_MX53
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_H1_UCTRL_H1_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H1_UCTRL_H1_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
|
||||
|
||||
v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET);
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_H1_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H1_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */
|
||||
else
|
||||
@@ -186,24 +245,59 @@ int mxc_set_usbcontrol(int port, unsigned int flags)
|
||||
break;
|
||||
case 2: /* Host 2 ULPI */
|
||||
v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET);
|
||||
#ifdef CONFIG_MX51
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */
|
||||
v &= ~MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask unused */
|
||||
else
|
||||
v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */
|
||||
|
||||
v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */
|
||||
#endif
|
||||
#ifdef CONFIG_MX53
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_H2_UCTRL_H2_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H2_UCTRL_H2_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is used */
|
||||
else
|
||||
v |= MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is not used */
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_H2_UCTRL_H2_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H2_UCTRL_H2_PWR_POL_BIT;
|
||||
#endif
|
||||
__raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET);
|
||||
break;
|
||||
#ifdef CONFIG_MX53
|
||||
case 3: /* Host 3 ULPI */
|
||||
v = __raw_readl(usbother_base + MXC_USBH3CTRL_OFFSET);
|
||||
if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)
|
||||
v |= MXC_H3_UCTRL_H3_OC_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H3_UCTRL_H3_OC_POL_BIT;
|
||||
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
|
||||
v &= ~MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is used */
|
||||
else
|
||||
v |= MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is not used */
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MXC_H3_UCTRL_H3_PWR_POL_BIT;
|
||||
else
|
||||
v &= ~MXC_H3_UCTRL_H3_PWR_POL_BIT;
|
||||
__raw_writel(v, usbother_base + MXC_USBH3CTRL_OFFSET);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
|
||||
int __weak board_ehci_hcd_init(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
|
||||
__attribute((weak, alias("__board_ehci_hcd_postinit")));
|
||||
void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
|
||||
{
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
|
||||
@@ -159,6 +159,11 @@ static void usbh1_oc_config(void)
|
||||
__raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
|
||||
}
|
||||
|
||||
int __weak board_ehci_hcd_init(int port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
{
|
||||
struct usb_ehci *ehci;
|
||||
|
||||
@@ -28,14 +28,22 @@
|
||||
|
||||
#define USBCTRL_OTGBASE_OFFSET 0x600
|
||||
|
||||
#ifdef CONFIG_MX25
|
||||
#define MX25_USB_CTRL_IP_PUE_DOWN_BIT (1<<6)
|
||||
#define MX25_USB_CTRL_HSTD_BIT (1<<5)
|
||||
#define MX25_USB_CTRL_USBTE_BIT (1<<4)
|
||||
#define MX25_USB_CTRL_OCPOL_OTG_BIT (1<<3)
|
||||
#endif
|
||||
#define MX25_OTG_SIC_SHIFT 29
|
||||
#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
|
||||
#define MX25_OTG_PM_BIT (1 << 24)
|
||||
#define MX25_OTG_PP_BIT (1 << 11)
|
||||
#define MX25_OTG_OCPOL_BIT (1 << 3)
|
||||
|
||||
#define MX25_H1_SIC_SHIFT 21
|
||||
#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
|
||||
#define MX25_H1_PP_BIT (1 << 18)
|
||||
#define MX25_H1_PM_BIT (1 << 16)
|
||||
#define MX25_H1_IPPUE_UP_BIT (1 << 7)
|
||||
#define MX25_H1_IPPUE_DOWN_BIT (1 << 6)
|
||||
#define MX25_H1_TLL_BIT (1 << 5)
|
||||
#define MX25_H1_USBTE_BIT (1 << 4)
|
||||
#define MX25_H1_OCPOL_BIT (1 << 2)
|
||||
|
||||
#ifdef CONFIG_MX31
|
||||
#define MX31_OTG_SIC_SHIFT 29
|
||||
#define MX31_OTG_SIC_MASK (0x3 << MX31_OTG_SIC_SHIFT)
|
||||
#define MX31_OTG_PM_BIT (1 << 24)
|
||||
@@ -49,59 +57,166 @@
|
||||
#define MX31_H1_SIC_MASK (0x3 << MX31_H1_SIC_SHIFT)
|
||||
#define MX31_H1_PM_BIT (1 << 8)
|
||||
#define MX31_H1_DT_BIT (1 << 4)
|
||||
#endif
|
||||
|
||||
#define MX35_OTG_SIC_SHIFT 29
|
||||
#define MX35_OTG_SIC_MASK (0x3 << MX35_OTG_SIC_SHIFT)
|
||||
#define MX35_OTG_PM_BIT (1 << 24)
|
||||
#define MX35_OTG_PP_BIT (1 << 11)
|
||||
#define MX35_OTG_OCPOL_BIT (1 << 3)
|
||||
|
||||
#define MX35_H1_SIC_SHIFT 21
|
||||
#define MX35_H1_SIC_MASK (0x3 << MX35_H1_SIC_SHIFT)
|
||||
#define MX35_H1_PP_BIT (1 << 18)
|
||||
#define MX35_H1_PM_BIT (1 << 16)
|
||||
#define MX35_H1_IPPUE_UP_BIT (1 << 7)
|
||||
#define MX35_H1_IPPUE_DOWN_BIT (1 << 6)
|
||||
#define MX35_H1_TLL_BIT (1 << 5)
|
||||
#define MX35_H1_USBTE_BIT (1 << 4)
|
||||
#define MX35_H1_OCPOL_BIT (1 << 2)
|
||||
|
||||
static int mxc_set_usbcontrol(int port, unsigned int flags)
|
||||
{
|
||||
unsigned int v;
|
||||
|
||||
#ifdef CONFIG_MX25
|
||||
v = MX25_USB_CTRL_IP_PUE_DOWN_BIT | MX25_USB_CTRL_HSTD_BIT |
|
||||
MX25_USB_CTRL_USBTE_BIT | MX25_USB_CTRL_OCPOL_OTG_BIT;
|
||||
v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
|
||||
#if defined(CONFIG_MX25)
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
v &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PM_BIT | MX25_OTG_PP_BIT |
|
||||
MX25_OTG_OCPOL_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX25_OTG_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX25_OTG_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX25_OTG_OCPOL_BIT;
|
||||
|
||||
break;
|
||||
case 1: /* H1 port */
|
||||
v &= ~(MX25_H1_SIC_MASK | MX25_H1_PM_BIT | MX25_H1_PP_BIT |
|
||||
MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
|
||||
MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT |
|
||||
MX25_H1_IPPUE_UP_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX25_H1_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX25_H1_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX25_H1_OCPOL_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX25_H1_TLL_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_INTERNAL_PHY)
|
||||
v |= MX25_H1_USBTE_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_DOWN)
|
||||
v |= MX25_H1_IPPUE_DOWN_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_UP)
|
||||
v |= MX25_H1_IPPUE_UP_BIT;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
#elif defined(CONFIG_MX31)
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_OTG_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_OTG_PM_BIT;
|
||||
|
||||
break;
|
||||
case 1: /* H1 port */
|
||||
v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT | MX31_H1_DT_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H1_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_H1_PM_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX31_H1_DT_BIT;
|
||||
|
||||
break;
|
||||
case 2: /* H2 port */
|
||||
v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT | MX31_H2_DT_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H2_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_H2_PM_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX31_H2_DT_BIT;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
#elif defined(CONFIG_MX35)
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
v &= ~(MX35_OTG_SIC_MASK | MX35_OTG_PM_BIT | MX35_OTG_PP_BIT |
|
||||
MX35_OTG_OCPOL_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_OTG_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX35_OTG_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX35_OTG_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX35_OTG_OCPOL_BIT;
|
||||
|
||||
break;
|
||||
case 1: /* H1 port */
|
||||
v &= ~(MX35_H1_SIC_MASK | MX35_H1_PM_BIT | MX35_H1_PP_BIT |
|
||||
MX35_H1_OCPOL_BIT | MX35_H1_TLL_BIT |
|
||||
MX35_H1_USBTE_BIT | MX35_H1_IPPUE_DOWN_BIT |
|
||||
MX35_H1_IPPUE_UP_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_H1_SIC_SHIFT;
|
||||
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX35_H1_PM_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH)
|
||||
v |= MX35_H1_PP_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW))
|
||||
v |= MX35_H1_OCPOL_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX35_H1_TLL_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_INTERNAL_PHY)
|
||||
v |= MX35_H1_USBTE_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_DOWN)
|
||||
v |= MX35_H1_IPPUE_DOWN_BIT;
|
||||
|
||||
if (flags & MXC_EHCI_IPPUE_UP)
|
||||
v |= MX35_H1_IPPUE_UP_BIT;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
#else
|
||||
#error MXC EHCI USB driver not supported on this platform
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MX31
|
||||
v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
|
||||
|
||||
switch (port) {
|
||||
case 0: /* OTG port */
|
||||
v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK)
|
||||
<< MX31_OTG_SIC_SHIFT;
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_OTG_PM_BIT;
|
||||
|
||||
break;
|
||||
case 1: /* H1 port */
|
||||
v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT |
|
||||
MX31_H1_DT_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK)
|
||||
<< MX31_H1_SIC_SHIFT;
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_H1_PM_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX31_H1_DT_BIT;
|
||||
|
||||
break;
|
||||
case 2: /* H2 port */
|
||||
v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT |
|
||||
MX31_H2_DT_BIT);
|
||||
v |= (flags & MXC_EHCI_INTERFACE_MASK)
|
||||
<< MX31_H2_SIC_SHIFT;
|
||||
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
|
||||
v |= MX31_H2_PM_BIT;
|
||||
|
||||
if (!(flags & MXC_EHCI_TTL_ENABLED))
|
||||
v |= MX31_H2_DT_BIT;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
writel(v, IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -119,13 +234,17 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
|
||||
udelay(80);
|
||||
|
||||
ehci = (struct usb_ehci *)(IMX_USB_BASE +
|
||||
(0x200 * CONFIG_MXC_USB_PORT));
|
||||
IMX_USB_PORT_OFFSET * CONFIG_MXC_USB_PORT);
|
||||
*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
|
||||
*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
|
||||
HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
|
||||
setbits_le32(&ehci->usbmode, CM_HOST);
|
||||
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
|
||||
mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);
|
||||
#ifdef CONFIG_MX35
|
||||
/* Workaround for ENGcm11601 */
|
||||
__raw_writel(0, &ehci->sbuscfg);
|
||||
#endif
|
||||
|
||||
udelay(10000);
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ COBJS-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o
|
||||
COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
|
||||
COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o
|
||||
COBJS-$(CONFIG_S6E63D6) += s6e63d6.o
|
||||
COBJS-$(CONFIG_LD9040) += ld9040.o
|
||||
COBJS-$(CONFIG_SED156X) += sed156x.o
|
||||
COBJS-$(CONFIG_VIDEO_AMBA) += amba.o
|
||||
COBJS-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o
|
||||
@@ -50,6 +51,7 @@ COBJS-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
|
||||
COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o
|
||||
COBJS-$(CONFIG_VIDEO_SM501) += sm501.o
|
||||
COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o
|
||||
COBJS-$(CONFIG_VIDEO_TEGRA) += tegra.o
|
||||
COBJS-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
|
||||
|
||||
COBJS := $(sort $(COBJS-y))
|
||||
|
||||
@@ -70,8 +70,19 @@ static void draw_logo(void)
|
||||
int x, y;
|
||||
ulong addr;
|
||||
|
||||
x = ((panel_width - panel_info.logo_width) >> 1);
|
||||
y = ((panel_height - panel_info.logo_height) >> 1) - 4;
|
||||
if (panel_width >= panel_info.logo_width) {
|
||||
x = ((panel_width - panel_info.logo_width) >> 1);
|
||||
} else {
|
||||
x = 0;
|
||||
printf("Warning: image width is bigger than display width\n");
|
||||
}
|
||||
|
||||
if (panel_height >= panel_info.logo_height) {
|
||||
y = ((panel_height - panel_info.logo_height) >> 1) - 4;
|
||||
} else {
|
||||
y = 0;
|
||||
printf("Warning: image height is bigger than display height\n");
|
||||
}
|
||||
|
||||
addr = panel_info.logo_addr;
|
||||
bmp_display(addr, x, y);
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
#define IPU_TPM_REG_BASE 0x01060000
|
||||
#define IPU_DC_TMPL_REG_BASE 0x01080000
|
||||
#define IPU_ISP_TBPR_REG_BASE 0x010C0000
|
||||
#elif defined(CONFIG_MX6Q)
|
||||
#elif defined(CONFIG_MX6)
|
||||
#define IPU_CPMEM_REG_BASE 0x00100000
|
||||
#define IPU_LUT_REG_BASE 0x00120000
|
||||
#define IPU_SRM_REG_BASE 0x00140000
|
||||
|
||||
144
drivers/video/ld9040.c
Normal file
144
drivers/video/ld9040.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* ld9040 AMOLED LCD panel driver.
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Donghwa Lee <dh09.lee@samsung.com>
|
||||
*
|
||||
* 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 <common.h>
|
||||
#include <spi.h>
|
||||
|
||||
static const unsigned char SEQ_SWRESET[] = {
|
||||
0x01,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_USER_SETTING[] = {
|
||||
0xF0, 0x5A, 0x5A
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_ELVSS_ON[] = {
|
||||
0xB1, 0x0D, 0x00, 0x16,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_TEMP_SWIRE[] = {
|
||||
0xB2, 0x06, 0x06, 0x06, 0x06,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_GTCON[] = {
|
||||
0xF7, 0x09, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_PANEL_CONDITION[] = {
|
||||
0xF8, 0x05, 0x65, 0x96, 0x71, 0x7D, 0x19, 0x3B,
|
||||
0x0D, 0x19, 0x7E, 0x0D, 0xE2, 0x00, 0x00, 0x7E,
|
||||
0x7D, 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_GAMMA_SET1[] = {
|
||||
0xF9, 0x00, 0xA7, 0xB4, 0xAE, 0xBF, 0x00, 0x91,
|
||||
0x00, 0xB2, 0xB4, 0xAA, 0xBB, 0x00, 0xAC, 0x00,
|
||||
0xB3, 0xB1, 0xAA, 0xBC, 0x00, 0xB3,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_GAMMA_CTRL[] = {
|
||||
0xFB, 0x02, 0x5A,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_APON[] = {
|
||||
0xF3, 0x00, 0x00, 0x00, 0x0A, 0x02,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_DISPCTL[] = {
|
||||
0xF2, 0x02, 0x08, 0x08, 0x10, 0x10,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_MANPWR[] = {
|
||||
0xB0, 0x04,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_PWR_CTRL[] = {
|
||||
0xF4, 0x0A, 0x87, 0x25, 0x6A, 0x44, 0x02, 0x88,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_SLPOUT[] = {
|
||||
0x11,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_SLPIN[] = {
|
||||
0x10,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_DISPON[] = {
|
||||
0x29,
|
||||
};
|
||||
|
||||
static const unsigned char SEQ_DISPOFF[] = {
|
||||
0x28,
|
||||
};
|
||||
|
||||
static void ld9040_spi_write(const unsigned char *wbuf, unsigned int size_cmd)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/*
|
||||
* Data are transmitted in 9-bit words:
|
||||
* the first bit is command/parameter, the other are the value.
|
||||
* The value's LSB is shifted to MSB position, to be sent as 9th bit
|
||||
*/
|
||||
|
||||
unsigned int data_out = 0, data_in = 0;
|
||||
for (i = 0; i < size_cmd; i++) {
|
||||
data_out = wbuf[i] >> 1;
|
||||
if (i != 0)
|
||||
data_out += 0x0080;
|
||||
if (wbuf[i] & 0x01)
|
||||
data_out += 0x8000;
|
||||
spi_xfer(NULL, 9, &data_out, &data_in, SPI_XFER_BEGIN);
|
||||
}
|
||||
}
|
||||
|
||||
void ld9040_cfg_ldo(void)
|
||||
{
|
||||
udelay(10);
|
||||
|
||||
ld9040_spi_write(SEQ_USER_SETTING,
|
||||
ARRAY_SIZE(SEQ_USER_SETTING));
|
||||
ld9040_spi_write(SEQ_PANEL_CONDITION,
|
||||
ARRAY_SIZE(SEQ_PANEL_CONDITION));
|
||||
ld9040_spi_write(SEQ_DISPCTL, ARRAY_SIZE(SEQ_DISPCTL));
|
||||
ld9040_spi_write(SEQ_MANPWR, ARRAY_SIZE(SEQ_MANPWR));
|
||||
ld9040_spi_write(SEQ_PWR_CTRL, ARRAY_SIZE(SEQ_PWR_CTRL));
|
||||
ld9040_spi_write(SEQ_ELVSS_ON, ARRAY_SIZE(SEQ_ELVSS_ON));
|
||||
ld9040_spi_write(SEQ_GTCON, ARRAY_SIZE(SEQ_GTCON));
|
||||
ld9040_spi_write(SEQ_GAMMA_SET1, ARRAY_SIZE(SEQ_GAMMA_SET1));
|
||||
ld9040_spi_write(SEQ_GAMMA_CTRL, ARRAY_SIZE(SEQ_GAMMA_CTRL));
|
||||
ld9040_spi_write(SEQ_SLPOUT, ARRAY_SIZE(SEQ_SLPOUT));
|
||||
|
||||
udelay(120);
|
||||
}
|
||||
|
||||
void ld9040_enable_ldo(unsigned int onoff)
|
||||
{
|
||||
if (onoff)
|
||||
ld9040_spi_write(SEQ_DISPON, ARRAY_SIZE(SEQ_DISPON));
|
||||
else
|
||||
ld9040_spi_write(SEQ_DISPOFF, ARRAY_SIZE(SEQ_DISPOFF));
|
||||
}
|
||||
379
drivers/video/tegra.c
Normal file
379
drivers/video/tegra.c
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
* 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 <common.h>
|
||||
#include <fdtdec.h>
|
||||
#include <lcd.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/funcmux.h>
|
||||
#include <asm/arch/pinmux.h>
|
||||
#include <asm/arch/pwm.h>
|
||||
#include <asm/arch/display.h>
|
||||
#include <asm/arch-tegra/timer.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* These are the stages we go throuh in enabling the LCD */
|
||||
enum stage_t {
|
||||
STAGE_START,
|
||||
STAGE_PANEL_VDD,
|
||||
STAGE_LVDS,
|
||||
STAGE_BACKLIGHT_VDD,
|
||||
STAGE_PWM,
|
||||
STAGE_BACKLIGHT_EN,
|
||||
STAGE_DONE,
|
||||
};
|
||||
|
||||
static enum stage_t stage; /* Current stage we are at */
|
||||
static unsigned long timer_next; /* Time we can move onto next stage */
|
||||
|
||||
/* Our LCD config, set up in handle_stage() */
|
||||
static struct fdt_panel_config config;
|
||||
struct fdt_disp_config *disp_config; /* Display controller config */
|
||||
|
||||
enum {
|
||||
/* Maximum LCD size we support */
|
||||
LCD_MAX_WIDTH = 1366,
|
||||
LCD_MAX_HEIGHT = 768,
|
||||
LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
|
||||
};
|
||||
|
||||
int lcd_line_length;
|
||||
int lcd_color_fg;
|
||||
int lcd_color_bg;
|
||||
|
||||
void *lcd_base; /* Start of framebuffer memory */
|
||||
void *lcd_console_address; /* Start of console buffer */
|
||||
|
||||
short console_col;
|
||||
short console_row;
|
||||
|
||||
vidinfo_t panel_info = {
|
||||
/* Insert a value here so that we don't end up in the BSS */
|
||||
.vl_col = -1,
|
||||
};
|
||||
|
||||
char lcd_cursor_enabled;
|
||||
|
||||
ushort lcd_cursor_width;
|
||||
ushort lcd_cursor_height;
|
||||
|
||||
#ifndef CONFIG_OF_CONTROL
|
||||
#error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support"
|
||||
#endif
|
||||
|
||||
void lcd_cursor_size(ushort width, ushort height)
|
||||
{
|
||||
lcd_cursor_width = width;
|
||||
lcd_cursor_height = height;
|
||||
}
|
||||
|
||||
void lcd_toggle_cursor(void)
|
||||
{
|
||||
ushort x, y;
|
||||
uchar *dest;
|
||||
ushort row;
|
||||
|
||||
x = console_col * lcd_cursor_width;
|
||||
y = console_row * lcd_cursor_height;
|
||||
dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_BPP) /
|
||||
8);
|
||||
|
||||
for (row = 0; row < lcd_cursor_height; ++row, dest += lcd_line_length) {
|
||||
ushort *d = (ushort *)dest;
|
||||
ushort color;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < lcd_cursor_width; ++i) {
|
||||
color = *d;
|
||||
color ^= lcd_color_fg;
|
||||
*d = color;
|
||||
++d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_cursor_on(void)
|
||||
{
|
||||
lcd_cursor_enabled = 1;
|
||||
lcd_toggle_cursor();
|
||||
}
|
||||
void lcd_cursor_off(void)
|
||||
{
|
||||
lcd_cursor_enabled = 0;
|
||||
lcd_toggle_cursor();
|
||||
}
|
||||
|
||||
char lcd_is_cursor_enabled(void)
|
||||
{
|
||||
return lcd_cursor_enabled;
|
||||
}
|
||||
|
||||
static void update_panel_size(struct fdt_disp_config *config)
|
||||
{
|
||||
panel_info.vl_col = config->width;
|
||||
panel_info.vl_row = config->height;
|
||||
panel_info.vl_bpix = config->log2_bpp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main init function called by lcd driver.
|
||||
* Inits and then prints test pattern if required.
|
||||
*/
|
||||
|
||||
void lcd_ctrl_init(void *lcdbase)
|
||||
{
|
||||
int line_length, size;
|
||||
int type = DCACHE_OFF;
|
||||
|
||||
assert(disp_config);
|
||||
|
||||
lcd_base = (void *)disp_config->frame_buffer;
|
||||
|
||||
/* Make sure that we can acommodate the selected LCD */
|
||||
assert(disp_config->width <= LCD_MAX_WIDTH);
|
||||
assert(disp_config->height <= LCD_MAX_HEIGHT);
|
||||
assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP);
|
||||
if (disp_config->width <= LCD_MAX_WIDTH
|
||||
&& disp_config->height <= LCD_MAX_HEIGHT
|
||||
&& disp_config->log2_bpp <= LCD_MAX_LOG2_BPP)
|
||||
update_panel_size(disp_config);
|
||||
size = lcd_get_size(&line_length);
|
||||
|
||||
/* Set up the LCD caching as requested */
|
||||
if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH)
|
||||
type = DCACHE_WRITETHROUGH;
|
||||
else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK)
|
||||
type = DCACHE_WRITEBACK;
|
||||
mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type);
|
||||
|
||||
/* Enable flushing after LCD writes if requested */
|
||||
lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH);
|
||||
|
||||
debug("LCD frame buffer at %p\n", lcd_base);
|
||||
}
|
||||
|
||||
ulong calc_fbsize(void)
|
||||
{
|
||||
return (panel_info.vl_col * panel_info.vl_row *
|
||||
NBITS(panel_info.vl_bpix)) / 8;
|
||||
}
|
||||
|
||||
void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
|
||||
{
|
||||
}
|
||||
|
||||
void tegra_lcd_early_init(const void *blob)
|
||||
{
|
||||
/*
|
||||
* Go with the maximum size for now. We will fix this up after
|
||||
* relocation. These values are only used for memory alocation.
|
||||
*/
|
||||
panel_info.vl_col = LCD_MAX_WIDTH;
|
||||
panel_info.vl_row = LCD_MAX_HEIGHT;
|
||||
panel_info.vl_bpix = LCD_MAX_LOG2_BPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the panel information from the fdt.
|
||||
*
|
||||
* @param blob fdt blob
|
||||
* @param config structure to store fdt config into
|
||||
* @return 0 if ok, -ve on error
|
||||
*/
|
||||
static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config)
|
||||
{
|
||||
int display_node;
|
||||
|
||||
disp_config = tegra_display_get_config();
|
||||
if (!disp_config) {
|
||||
debug("%s: Display controller is not configured\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
display_node = disp_config->panel_node;
|
||||
if (display_node < 0) {
|
||||
debug("%s: No panel configuration available\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm");
|
||||
if (config->pwm_channel < 0) {
|
||||
debug("%s: Unable to request PWM channel\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->cache_type = fdtdec_get_int(blob, display_node,
|
||||
"nvidia,cache-type",
|
||||
FDT_LCD_CACHE_WRITE_BACK_FLUSH);
|
||||
|
||||
/* These GPIOs are all optional */
|
||||
fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios",
|
||||
&config->backlight_en);
|
||||
fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios",
|
||||
&config->lvds_shutdown);
|
||||
fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios",
|
||||
&config->backlight_vdd);
|
||||
fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios",
|
||||
&config->panel_vdd);
|
||||
|
||||
return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings",
|
||||
config->panel_timings, FDT_LCD_TIMINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the next stage of device init
|
||||
*/
|
||||
static int handle_stage(const void *blob)
|
||||
{
|
||||
debug("%s: stage %d\n", __func__, stage);
|
||||
|
||||
/* do the things for this stage */
|
||||
switch (stage) {
|
||||
case STAGE_START:
|
||||
/* Initialize the Tegra display controller */
|
||||
if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) {
|
||||
printf("%s: Failed to probe display driver\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get panel details */
|
||||
if (fdt_decode_lcd(blob, &config)) {
|
||||
printf("No valid LCD information in device tree\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is possible that the FDT has requested that the LCD be
|
||||
* disabled. We currently don't support this. It would require
|
||||
* changes to U-Boot LCD subsystem to have LCD support
|
||||
* compiled in but not used. An easier option might be to
|
||||
* still have a frame buffer, but leave the backlight off and
|
||||
* remove all mention of lcd in the stdout environment
|
||||
* variable.
|
||||
*/
|
||||
|
||||
funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
|
||||
|
||||
fdtdec_setup_gpio(&config.panel_vdd);
|
||||
fdtdec_setup_gpio(&config.lvds_shutdown);
|
||||
fdtdec_setup_gpio(&config.backlight_vdd);
|
||||
fdtdec_setup_gpio(&config.backlight_en);
|
||||
|
||||
/*
|
||||
* TODO: If fdt includes output flag we can omit this code
|
||||
* since fdtdec_setup_gpio will do it for us.
|
||||
*/
|
||||
if (fdt_gpio_isvalid(&config.panel_vdd))
|
||||
gpio_direction_output(config.panel_vdd.gpio, 0);
|
||||
if (fdt_gpio_isvalid(&config.lvds_shutdown))
|
||||
gpio_direction_output(config.lvds_shutdown.gpio, 0);
|
||||
if (fdt_gpio_isvalid(&config.backlight_vdd))
|
||||
gpio_direction_output(config.backlight_vdd.gpio, 0);
|
||||
if (fdt_gpio_isvalid(&config.backlight_en))
|
||||
gpio_direction_output(config.backlight_en.gpio, 0);
|
||||
break;
|
||||
case STAGE_PANEL_VDD:
|
||||
if (fdt_gpio_isvalid(&config.panel_vdd))
|
||||
gpio_direction_output(config.panel_vdd.gpio, 1);
|
||||
break;
|
||||
case STAGE_LVDS:
|
||||
if (fdt_gpio_isvalid(&config.lvds_shutdown))
|
||||
gpio_set_value(config.lvds_shutdown.gpio, 1);
|
||||
break;
|
||||
case STAGE_BACKLIGHT_VDD:
|
||||
if (fdt_gpio_isvalid(&config.backlight_vdd))
|
||||
gpio_set_value(config.backlight_vdd.gpio, 1);
|
||||
break;
|
||||
case STAGE_PWM:
|
||||
/* Enable PWM at 15/16 high, 32768 Hz with divider 1 */
|
||||
pinmux_set_func(PINGRP_GPU, PMUX_FUNC_PWM);
|
||||
pinmux_tristate_disable(PINGRP_GPU);
|
||||
|
||||
pwm_enable(config.pwm_channel, 32768, 0xdf, 1);
|
||||
break;
|
||||
case STAGE_BACKLIGHT_EN:
|
||||
if (fdt_gpio_isvalid(&config.backlight_en))
|
||||
gpio_set_value(config.backlight_en.gpio, 1);
|
||||
break;
|
||||
case STAGE_DONE:
|
||||
break;
|
||||
}
|
||||
|
||||
/* set up timer for next stage */
|
||||
timer_next = timer_get_us();
|
||||
if (stage < FDT_LCD_TIMINGS)
|
||||
timer_next += config.panel_timings[stage] * 1000;
|
||||
|
||||
/* move to next stage */
|
||||
stage++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tegra_lcd_check_next_stage(const void *blob, int wait)
|
||||
{
|
||||
if (stage == STAGE_DONE)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
/* wait if we need to */
|
||||
debug("%s: stage %d\n", __func__, stage);
|
||||
if (stage != STAGE_START) {
|
||||
int delay = timer_next - timer_get_us();
|
||||
|
||||
if (delay > 0) {
|
||||
if (wait)
|
||||
udelay(delay);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (handle_stage(blob))
|
||||
return -1;
|
||||
} while (wait && stage != STAGE_DONE);
|
||||
if (stage == STAGE_DONE)
|
||||
debug("%s: LCD init complete\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lcd_enable(void)
|
||||
{
|
||||
/*
|
||||
* Backlight and power init will be done separately in
|
||||
* tegra_lcd_check_next_stage(), which should be called in
|
||||
* board_late_init().
|
||||
*
|
||||
* U-Boot code supports only colour depth, selected at compile time.
|
||||
* The device tree setting should match this. Otherwise the display
|
||||
* will not look right, and U-Boot may crash.
|
||||
*/
|
||||
if (disp_config->log2_bpp != LCD_BPP) {
|
||||
printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)"
|
||||
" must match setting of LCD_BPP (%d)\n", __func__,
|
||||
disp_config->log2_bpp, disp_config->bpp, LCD_BPP);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ LIB := $(obj)libwatchdog.o
|
||||
COBJS-$(CONFIG_AT91SAM9_WATCHDOG) += at91sam9_wdt.o
|
||||
COBJS-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o
|
||||
COBJS-$(CONFIG_TNETV107X_WATCHDOG) += tnetv107x_wdt.o
|
||||
COBJS-$(CONFIG_S5P) += s5p_wdt.o
|
||||
|
||||
COBJS := $(COBJS-y)
|
||||
SRCS := $(COBJS:.o=.c)
|
||||
|
||||
59
drivers/watchdog/s5p_wdt.c
Normal file
59
drivers/watchdog/s5p_wdt.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Minkyu Kang <mk7.kang@samsung.com>
|
||||
*
|
||||
* 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 <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/watchdog.h>
|
||||
|
||||
#define PRESCALER_VAL 255
|
||||
|
||||
void wdt_stop(void)
|
||||
{
|
||||
struct s5p_watchdog *wdt =
|
||||
(struct s5p_watchdog *)samsung_get_base_watchdog();
|
||||
unsigned int wtcon;
|
||||
|
||||
wtcon = readl(&wdt->wtcon);
|
||||
wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET);
|
||||
|
||||
writel(wtcon, &wdt->wtcon);
|
||||
}
|
||||
|
||||
void wdt_start(unsigned int timeout)
|
||||
{
|
||||
struct s5p_watchdog *wdt =
|
||||
(struct s5p_watchdog *)samsung_get_base_watchdog();
|
||||
unsigned int wtcon;
|
||||
|
||||
wdt_stop();
|
||||
|
||||
wtcon = readl(&wdt->wtcon);
|
||||
wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128));
|
||||
wtcon &= ~WTCON_INT;
|
||||
wtcon |= WTCON_RESET;
|
||||
wtcon |= WTCON_PRESCALER(PRESCALER_VAL);
|
||||
|
||||
writel(timeout, &wdt->wtdat);
|
||||
writel(timeout, &wdt->wtcnt);
|
||||
writel(wtcon, &wdt->wtcon);
|
||||
}
|
||||
Reference in New Issue
Block a user