blob: 894b0b9a145f2302fdf9d5fd5087d04eb9b04260 [file] [log] [blame]
/*
* maxim_codec.c -- MAXIM CODEC Common driver
*
* Copyright 2011 Maxim Integrated Products
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <asm/io.h>
#include <common.h>
#if !defined(CONFIG_TEGRA)
#include <asm/arch/clk.h>
#include <asm/arch/cpu.h>
#include <asm/arch/power.h>
#endif
#include <asm/gpio.h>
#include <div64.h>
#include <fdtdec.h>
#include <i2c.h>
#include <sound.h>
#include "i2s.h"
#include "maxim_codec.h"
#include "max98095.h"
#include "max98090.h"
struct sound_codec_info g_codec_info;
struct maxim_codec_priv g_maxim_codec_info;
static unsigned int g_maxim_codec_i2c_dev_addr;
/*
* 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.
*/
int maxim_codec_i2c_write(unsigned int reg, unsigned char data)
{
debug("%s: Write Addr : 0x%02X, Data : 0x%02X\n",
__func__, reg, data);
return i2c_write(g_maxim_codec_i2c_dev_addr, reg, 1, &data, 1);
}
/*
* 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.
*/
unsigned int maxim_codec_i2c_read(unsigned int reg, unsigned char *data)
{
int ret;
ret = i2c_read(g_maxim_codec_i2c_dev_addr, reg, 1, data, 1);
if (ret != 0) {
debug("%s: Error while reading register %#04x\n",
__func__, reg);
return -1;
}
return 0;
}
/*
* update device register bits through i2c
*
* @param reg codec register
* @param mask register mask
* @param value new value
*
* @return int value 0 for success, non-zero error code.
*/
int maxim_codec_update_bits(unsigned int reg, unsigned char mask,
unsigned char value)
{
int change, ret = 0;
unsigned char old, new;
if (maxim_codec_i2c_read(reg, &old) != 0)
return -1;
new = (old & ~mask) | (value & mask);
change = (old != new) ? 1 : 0;
if (change)
ret = maxim_codec_i2c_write(reg, new);
if (ret < 0)
return ret;
return change;
}
static int maxim_codec_do_init(struct sound_codec_info *pcodec_info,
int sampling_rate, int mclk_freq,
int bits_per_sample)
{
int ret = 0;
#if !defined(CONFIG_TEGRA)
/* Enable codec clock */
set_xclkout();
/* shift the device address by 1 for 7 bit addressing */
g_maxim_codec_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1;
#else /* TEGRA */
g_maxim_codec_i2c_dev_addr = pcodec_info->i2c_dev_addr;
#endif
if (pcodec_info->codec_type == CODEC_MAX_98095) {
g_maxim_codec_info.devtype = MAX98095;
ret = max98095_device_init(&g_maxim_codec_info);
} else if (pcodec_info->codec_type == CODEC_MAX_98090) {
g_maxim_codec_info.devtype = MAX98090;
ret = max98090_device_init(&g_maxim_codec_info);
} else {
printf("%s: Codec id [%d] not defined\n", __func__,
pcodec_info->codec_type);
return -1;
}
if (ret < 0) {
debug("%s: maxim codec chip init failed\n", __func__);
return ret;
}
if (pcodec_info->codec_type == CODEC_MAX_98095) {
ret = max98095_set_sysclk(&g_maxim_codec_info, mclk_freq);
if (ret < 0) {
debug("%s: max98095 codec set sys clock failed\n",
__func__);
return ret;
}
ret = max98095_hw_params(&g_maxim_codec_info, sampling_rate,
bits_per_sample);
if (ret == 0) {
ret = max98095_set_fmt(&g_maxim_codec_info,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
}
} else if (pcodec_info->codec_type == CODEC_MAX_98090) {
ret = max98090_set_sysclk(&g_maxim_codec_info, mclk_freq);
if (ret < 0) {
debug("%s: max98090 codec set sys clock failed\n",
__func__);
return ret;
}
ret = max98090_hw_params(&g_maxim_codec_info, sampling_rate,
bits_per_sample);
if (ret == 0) {
ret = max98090_set_fmt(&g_maxim_codec_info,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
}
}
return ret;
}
static int get_maxim_codec_values(struct sound_codec_info *pcodec_info,
const void *blob, const char *codectype)
{
int error = 0;
#ifdef CONFIG_OF_CONTROL
enum fdt_compat_id compat;
int node;
int parent;
/* Get the node from FDT for codec */
node = fdtdec_next_compatible(blob, 0, COMPAT_MAXIM_98095_CODEC);
if (node <= 0) {
debug("%s: No node for Max98095 codec in device tree\n",
__func__);
debug("node = %d\n", node);
node = fdtdec_next_compatible(blob, 0,
COMPAT_MAXIM_98090_CODEC);
if (node <= 0) {
debug("%s: No node for Max98090 codec in device tree\n",
__func__);
debug("node = %d\n", node);
return -1;
}
}
parent = fdt_parent_offset(blob, node);
if (parent < 0) {
debug("%s: Cannot find node parent\n", __func__);
return -1;
}
compat = fdtdec_lookup(blob, parent);
switch (compat) {
case COMPAT_SAMSUNG_S3C2440_I2C:
case COMPAT_SAMSUNG_EXYNOS5_I2C:
case COMPAT_NVIDIA_TEGRA114_I2C:
pcodec_info->i2c_bus = i2c_get_bus_num_fdt(parent);
error |= pcodec_info->i2c_bus;
debug("i2c bus = %d\n", pcodec_info->i2c_bus);
pcodec_info->i2c_dev_addr = fdtdec_get_int(blob, node,
"reg", 0);
error |= pcodec_info->i2c_dev_addr;
debug("i2c dev addr = %x\n", pcodec_info->i2c_dev_addr);
break;
default:
debug("%s: Unknown compat id %d\n", __func__, compat);
return -1;
}
#else
pcodec_info->i2c_bus = MAXIM_AUDIO_I2C_BUS;
pcodec_info->i2c_dev_addr = MAXIM_AUDIO_I2C_REG;
debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr);
#endif
if (!strcmp(codectype, "max98095"))
pcodec_info->codec_type = CODEC_MAX_98095;
else if (!strcmp(codectype, "max98090"))
pcodec_info->codec_type = CODEC_MAX_98090;
if (error == -1) {
debug("fail to get max98095 codec node properties\n");
return -1;
}
return 0;
}
/* maxim Device Initialisation */
int maxim_codec_init(const void *blob, const char *codectype, int sampling_rate,
int mclk_freq, int bits_per_sample)
{
int ret;
int old_bus = i2c_get_bus_num();
struct sound_codec_info *pcodec_info = &g_codec_info;
if (get_maxim_codec_values(pcodec_info, blob, codectype) < 0) {
printf("FDT Codec values failed\n");
return -1;
}
i2c_set_bus_num(pcodec_info->i2c_bus);
ret = maxim_codec_do_init(pcodec_info, sampling_rate, mclk_freq,
bits_per_sample);
i2c_set_bus_num(old_bus);
return ret;
}