blob: add00289484339068fce766d5a7942875c620641 [file] [log] [blame]
/*
* Copyright (C) 2015 Google Inc.
*
* 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; version 2 of the License.
*
* 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.
*/
/*
* Analog Devices SSM4567 Audio Amplifier Driver
* www.analog.com/media/en/technical-documentation/data-sheets/SSM4567.pdf
*/
#include <stdio.h>
#include "base/time.h"
#include "base/xalloc.h"
#include "drivers/bus/i2c/i2c.h"
#include "drivers/sound/ssm4567.h"
static int ssm4567_read(ssm4567Codec *codec, uint8_t reg, uint8_t *data)
{
return i2c_read8(codec->i2c, codec->chip, reg, data);
}
static int ssm4567_write(ssm4567Codec *codec, uint8_t reg, uint8_t data)
{
return i2c_write8(codec->i2c, codec->chip, reg, data);
}
static int ssm4567_update(ssm4567Codec *codec, uint8_t reg,
uint8_t mask, uint8_t value)
{
uint8_t old, new_value;
if (codec->read(codec, reg, &old)) {
printf("%s: Error reading reg 0x%02x!\n", __func__, reg);
return 1;
}
new_value = (old & ~mask) | (value & mask);
if (old != new_value && codec->write(codec, reg, new_value)) {
printf("%s: Error writing reg 0x%02x to 0x%02x\n",
__func__, reg, new_value);
return 1;
}
return 0;
}
static int ssm4567_set_mode(ssm4567Codec *codec)
{
uint8_t sai1_mask, sai1_value;
switch (codec->mode) {
case SSM4567_MODE_PDM:
/* Disable PDM Pattern Control */
codec->update(codec, SSM4567_PDM_CTRL,
SSM4567_PDM_CTRL_PAT_CTRL_EN, 0);
sai1_mask = SSM4567_SAI_CTRL_1_PDM_MODE;
sai1_value = SSM4567_SAI_CTRL_1_PDM_MODE_PDM;
break;
case SSM4567_MODE_TDM_PCM:
sai1_mask = SSM4567_SAI_CTRL_1_PDM_MODE |
SSM4567_SAI_CTRL_1_SAI_MODE;
sai1_value = SSM4567_SAI_CTRL_1_PDM_MODE_SERIAL |
SSM4567_SAI_CTRL_1_SAI_MODE_TDM_PCM;
break;
case SSM4567_MODE_I2S:
sai1_mask = SSM4567_SAI_CTRL_1_PDM_MODE |
SSM4567_SAI_CTRL_1_SAI_MODE |
SSM4567_SAI_CTRL_1_SDATA_FMT;
sai1_value = SSM4567_SAI_CTRL_1_PDM_MODE_SERIAL |
SSM4567_SAI_CTRL_1_SAI_MODE_TDM_PCM |
SSM4567_SAI_CTRL_1_SDATA_FMT_I2S;
break;
case SSM4567_MODE_I2S_LEFT:
sai1_mask = SSM4567_SAI_CTRL_1_PDM_MODE |
SSM4567_SAI_CTRL_1_SAI_MODE |
SSM4567_SAI_CTRL_1_SDATA_FMT;
sai1_value = SSM4567_SAI_CTRL_1_PDM_MODE_SERIAL |
SSM4567_SAI_CTRL_1_SAI_MODE_TDM_PCM |
SSM4567_SAI_CTRL_1_SDATA_FMT_LEFT;
break;
default:
printf("%s: Illegal mode %d.\n", __func__, codec->mode);
return 1;
}
/* Setup Serial Audio Interface */
if (codec->update(codec, SSM4567_SAI_CTRL_1, sai1_mask, sai1_value)) {
printf("%s: Error setting Serial Audio Interface\n", __func__);
return 1;
}
return 0;
}
/* Reset the audio codec */
static int ssm4567_reset(ssm4567Codec *codec)
{
if (codec->write(codec, SSM4567_SOFT_RESET, SSM4567_SOFT_RESET_VALUE)) {
printf("%s: Error resetting codec!\n", __func__);
return 1;
}
mdelay(20);
return 0;
}
/* Power up codec */
static int ssm4567_powerup(ssm4567Codec *codec)
{
if (codec->update(codec, SSM4567_POWER_CTRL,
SSM4567_POWER_CTRL_SPWDN, 0)) {
printf("%s: Error powering up codec\n", __func__);
return 1;
}
return 0;
}
/* Initialize codec device */
static int ssm4567_enable(SoundRouteComponentOps *me)
{
ssm4567Codec *codec = container_of(me, ssm4567Codec, component.ops);
int ret;
/* Reset the codec */
ret = ssm4567_reset(codec);
/* Power up codec */
ret |= ssm4567_powerup(codec);
/* Put codec into requested mode */
ret |= ssm4567_set_mode(codec);
return ret;
}
ssm4567Codec *new_ssm4567_codec(I2cOps *i2c, uint8_t chip,
ssm4567CodecMode mode)
{
ssm4567Codec *codec = xzalloc(sizeof(*codec));
codec->component.ops.enable = &ssm4567_enable;
codec->i2c = i2c;
codec->chip = chip;
codec->mode = mode;
codec->read = &ssm4567_read;
codec->write = &ssm4567_write;
codec->update = &ssm4567_update;
return codec;
}