blob: 8ff4269aff17406c549d906ab094d4d211e6fbb1 [file] [log] [blame]
/*
* max98095.c -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2013 Google Inc. All rights reserved.
* Copyright 2011 Maxim Integrated Products
*
* Modified for uboot by R. Chandrasekar (rcsekar@samsung.com)
*
* 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 <stdio.h>
#include "base/algorithm.h"
#include "base/xalloc.h"
#include "drivers/bus/i2c/i2c.h"
#include "drivers/sound/max98095.h"
static int max98095_i2c_write(Max98095Codec *codec, uint8_t reg, uint8_t data)
{
return i2c_write8(codec->i2c, codec->chip, reg, data);
}
static int max98095_i2c_read(Max98095Codec *codec, uint8_t reg, uint8_t *data)
{
return i2c_read8(codec->i2c, codec->chip, reg, data);
}
static int max98095_update_bits(Max98095Codec *codec, uint8_t reg,
uint8_t mask, uint8_t value)
{
uint8_t old;
if (max98095_i2c_read(codec, reg, &old))
return 1;
uint8_t new = (old & ~mask) | (value & mask);
if (old != new && max98095_i2c_write(codec, reg, new))
return 1;
return 0;
}
static int max98095_hw_params(Max98095Codec *codec)
{
switch (codec->bits_per_sample) {
case 16:
if (max98095_update_bits(codec, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, 0))
return 1;
if (max98095_update_bits(codec, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, 0))
return 1;
break;
case 24:
if (max98095_update_bits(codec, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, M98095_DAI_WS))
return 1;
if (max98095_update_bits(codec, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, M98095_DAI_WS))
return 1;
break;
default:
printf("%s: Illegal bits per sample %d.\n", __func__,
codec->bits_per_sample);
return 1;
}
// Index 0 is reserved.
static const int rate_table[] = {
0, 8000, 11025, 16000, 22050, 24000,
32000, 44100, 48000, 88200, 96000
};
static const int table_size = ARRAY_SIZE(rate_table);
uint8_t index = table_size;
for (int i = 0; i < table_size; i++) {
if (rate_table[i] >= codec->sample_rate) {
index = i;
break;
}
}
if (index == table_size) {
printf("%s: Failed to set sample rate %d.\n", __func__,
codec->sample_rate);
return 1;
}
if (max98095_update_bits(codec, M98095_027_DAI1_CLKMODE,
M98095_CLKMODE_MASK, index))
return 1;
if (max98095_update_bits(codec, M98095_031_DAI2_CLKMODE,
M98095_CLKMODE_MASK, index))
return 1;
/* Update sample rate mode */
int rate = (codec->sample_rate < 50000) ? 0 : M98095_DAI_DHF;
if (max98095_update_bits(codec, M98095_02E_DAI1_FILTERS,
M98095_DAI_DHF, rate))
return 1;
if (max98095_update_bits(codec, M98095_038_DAI2_FILTERS,
M98095_DAI_DHF, rate))
return 1;
return 0;
}
// Configures audio interface system clock for the selected frequency.
static int max98095_set_sysclk(Max98095Codec *codec)
{
int freq = codec->lr_frame_size * codec->sample_rate;
uint8_t mclksel = (codec->master_clock == 2) ? 1 : 0;
/*
* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 40MHz)..
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
if (freq >= 10 * MHz && freq < 20 * MHz) {
mclksel |= M98095_PSCLK_DIV1;
} else if (freq >= 20 * MHz && freq < 40 * MHz) {
mclksel |= M98095_PSCLK_DIV2;
} else if (freq >= 40 * MHz && freq < 60 * MHz) {
mclksel |= M98095_PSCLK_DIV4;
} else {
printf("%s: Invalid master clock frequency\n", __func__);
return 1;
}
if (max98095_i2c_write(codec, M98095_026_SYS_CLK, mclksel))
return 1;
printf("%s: Clock at %uHz on MCLK%d\n", __func__, freq,
codec->master_clock);
return 0;
}
// Sets Max98095 I2S format.
static int max98095_set_fmt(Max98095Codec *codec)
{
// Slave mode PLL.
if (max98095_i2c_write(codec, M98095_028_DAI1_CLKCFG_HI, 0x80) ||
max98095_i2c_write(codec, M98095_029_DAI1_CLKCFG_LO, 0x00))
return 1;
if (max98095_update_bits(codec, M98095_02A_DAI1_FORMAT,
M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
M98095_DAI_WCI, M98095_DAI_DLY))
return 1;
if (max98095_i2c_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64))
return 1;
if (max98095_i2c_write(codec, M98095_032_DAI2_CLKCFG_HI, 0x80) ||
max98095_i2c_write(codec, M98095_033_DAI2_CLKCFG_LO, 0x00))
return 1;
if (max98095_update_bits(codec, M98095_034_DAI2_FORMAT,
M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI |
M98095_DAI_WCI, M98095_DAI_DLY))
return 1;
if (max98095_i2c_write(codec, M98095_035_DAI2_CLOCK, M98095_DAI_BSEL64))
return 1;
return 0;
}
// Resets the audio codec.
static int max98095_reset(Max98095Codec *codec)
{
// Gracefully reset the DSP core and the codec hardware in a proper
// sequence.
if (max98095_i2c_write(codec, M98095_00F_HOST_CFG, 0) ||
max98095_i2c_write(codec, M98095_097_PWR_SYS, 0))
return 1;
// Reset to hardware default for registers, as there is not a soft
// reset hardware control register.
for (int i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++)
if (max98095_i2c_write(codec, i, 0))
return 1;
return 0;
}
// Intialize max98095 codec device.
static int max98095_device_init(Max98095Codec *codec)
{
// Reset the codec, the DSP core, and disable all interrupts.
if (max98095_reset(codec))
return 1;
uint8_t id;
if (max98095_i2c_read(codec, M98095_0FF_REV_ID, &id))
return 1;
printf("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A');
int res = 0;
res |= max98095_i2c_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
// Initialize registers to hardware default configuring audio
// interface 1 and 2 to DAC.
res |= max98095_i2c_write(codec, M98095_048_MIX_DAC_LR,
M98095_DAI1L_TO_DACL | M98095_DAI1R_TO_DACR |
M98095_DAI2M_TO_DACL | M98095_DAI2M_TO_DACR);
res |= max98095_i2c_write(codec, M98095_092_PWR_EN_OUT,
M98095_SPK_SPREADSPECTRUM);
res |= max98095_i2c_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL);
res |= max98095_i2c_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL);
res |= max98095_i2c_write(codec, M98095_02C_DAI1_IOCFG,
M98095_S1NORMAL | M98095_SDATA);
res |= max98095_i2c_write(codec, M98095_036_DAI2_IOCFG,
M98095_S2NORMAL | M98095_SDATA);
res |= max98095_i2c_write(codec, M98095_040_DAI3_IOCFG,
M98095_S3NORMAL | M98095_SDATA);
// Take the codec out of the shut down.
res |= max98095_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
M98095_SHDNRUN);
// Route DACL and DACR output to HO and speakers.
res |= max98095_i2c_write(codec, M98095_050_MIX_SPK_LEFT, 0x01); // DACL
res |= max98095_i2c_write(codec, M98095_051_MIX_SPK_RIGHT, 0x01);// DACR
res |= max98095_i2c_write(codec, M98095_04C_MIX_HP_LEFT, 0x01); // DACL
res |= max98095_i2c_write(codec, M98095_04D_MIX_HP_RIGHT, 0x01); // DACR
// Power enable.
res |= max98095_i2c_write(codec, M98095_091_PWR_EN_OUT, 0xF3);
// Set volume.
res |= max98095_i2c_write(codec, M98095_064_LVL_HP_L, 15);
res |= max98095_i2c_write(codec, M98095_065_LVL_HP_R, 15);
res |= max98095_i2c_write(codec, M98095_067_LVL_SPK_L, 16);
res |= max98095_i2c_write(codec, M98095_068_LVL_SPK_R, 16);
// Enable DAIs.
res |= max98095_i2c_write(codec, M98095_093_BIAS_CTRL, 0x30);
res |= max98095_i2c_write(codec, M98095_096_PWR_DAC_CK, 0x07);
return res;
}
static int max98095_enable(SoundRouteComponentOps *me)
{
Max98095Codec *codec = container_of(me, Max98095Codec, component.ops);
return max98095_device_init(codec) || max98095_set_sysclk(codec) ||
max98095_hw_params(codec) || max98095_set_fmt(codec);
}
Max98095Codec *new_max98095_codec(I2cOps *i2c, uint8_t chip,
int bits_per_sample, int sample_rate,
int lr_frame_size, uint8_t master_clock)
{
Max98095Codec *codec = xzalloc(sizeof(*codec));
codec->component.ops.enable = &max98095_enable;
codec->i2c = i2c;
codec->chip = chip;
codec->bits_per_sample = bits_per_sample;
codec->sample_rate = sample_rate;
codec->lr_frame_size = lr_frame_size;
codec->master_clock = master_clock;
return codec;
}