blob: 99379169a1a3491e60f5119cd2d7eb037d3e9816 [file] [log] [blame]
/*
* All Rights Reserved
*
* MARVELL CONFIDENTIAL
* Copyright 2012 Marvell International Ltd All Rights Reserved.
* The source code contained or described herein and all documents related to
* the source code ("Material") are owned by Marvell International Ltd or its
* suppliers or licensors. Title to the Material remains with Marvell International Ltd
* or its suppliers and licensors. The Material contains trade secrets and
* proprietary and confidential information of Marvell or its suppliers and
* licensors. The Material is protected by worldwide copyright and trade secret
* laws and treaty provisions. No part of the Material may be used, copied,
* reproduced, modified, published, uploaded, posted, transmitted, distributed,
* or disclosed in any way without Marvell's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by Marvell in writing.
*
*/
#define LOG_TAG "acm_ach_gelato"
#define LOG_NDEBUG 0
#define SYSCLK_DEV "/proc/driver/sspa_sysclk"
#include <cutils/log.h>
#include "acm_gelato.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int codec_reg_num = NUM_OF_REGS;
static void CODEC_Enable(void);
static void CODEC_Disable(void);
static void CODEC_Reset(void);
static void CODEC_GetTypeAndID(int *type, unsigned char *id);
static ACH_ReturnCode CODEC_Handle(void *setting);
ACH_ComponentHandler CODEC_handler = {
COMPONENT_INACTIVE,
0,
CODEC_Enable,
CODEC_Disable,
CODEC_Reset,
CODEC_GetTypeAndID,
CODEC_Handle,
};
extern void I2CWrite(unsigned char numid, unsigned short value);
extern void I2CBurstWrite(unsigned char numid, unsigned char size, unsigned char *value);
extern void I2CRead(unsigned char numid, unsigned char *value);
extern void I2CBurstRead(unsigned char numid, unsigned char *value, unsigned char *size);
static void CODEC_RegSet(unsigned char numid, unsigned char value) {
#ifndef DEBUG_FAKE_ACH
I2CWrite(numid, value);
#endif
}
static void CODEC_RegBurstSet(unsigned char numid, unsigned char size, unsigned char *value) {
#ifndef DEBUG_FAKE_ACH
I2CBurstWrite(numid, size, value);
#endif
}
void CODEC_RegGet(unsigned char numid, unsigned char *value) {
#ifndef DEBUG_FAKE_ACH
I2CRead(numid, value);
#endif
}
void CODEC_RegBurstGet(unsigned char numid, unsigned char *value, unsigned char *size) {
#ifndef DEBUG_FAKE_ACH
I2CBurstRead(numid, value, size);
#endif
}
static void CODEC_ResetRegCache() {
int size = sizeof(reg_cache) / sizeof(reg_cache[0]);
int i = 0;
for (i = 0; i < size; i++) {
reg_cache[i].reg_value = reg_cache[i].reg_default;
}
}
unsigned char CODEC_GetRegNumId(unsigned char reg_index) {
unsigned char i = 0;
for (i = 0; i < NUM_OF_REGS; i++) {
if (reg_cache[i].reg_index == reg_index) {
return REG_NUMID_BASE + i;
}
}
return 0;
}
//--------------------------------------------------------------
//-------- External ACH APIs
//--------------------------------------------------------------
static void CODEC_Enable(void) {
int mDev;
LOGI(CODEC_Enable, "%s: enable gelato component", __FUNCTION__);
/* Open sysclk */
mDev = open(SYSCLK_DEV, O_RDWR);
if (mDev < 0) {
LOGE(CODEC_Enable1, "%s: Failed to open %s\n", __FUNCTION__, SYSCLK_DEV);
} else {
write(mDev, "1", 1);
mDev = -1;
LOGI(CODEC_Enable2, "%s: open sysclk\n", __FUNCTION__);
}
/* To be implemented after power control regiter is supported in GELATO production version */
//usleep(2000);
CODEC_RegSet(0x11, 0x00);
CODEC_RegSet(0x11, 0x01);
CODEC_ResetRegCache();
CODEC_RegSet(0xc8, 0x01);
CODEC_RegSet(0x25, 0x00);
CODEC_RegSet(0xd4, 0xff);
reg_cache[0xc8].reg_value = 0x01;
reg_cache[0x25].reg_value = 0x00;
reg_cache[0xd4].reg_value = 0xff;
}
static void CODEC_Disable(void) {
int mDev;
LOGI(CODEC_Disable, "%s: disable gelato component", __FUNCTION__);
/* disable sysclk */
mDev = open(SYSCLK_DEV, O_RDWR);
if (mDev < 0) {
LOGE(CODEC_Disable1, "%s: Failed to open %s\n", __FUNCTION__, SYSCLK_DEV);
} else {
/* now there's a work around, as EDEN Z1 use gelato to do HS detection, we cannot shut down its work clock, otherwise HS detection function will not work */
//write(mDev, "0", 1);
mDev = -1;
LOGI(CODEC_Disable2, "%s: disable sysclk\n", __FUNCTION__);
}
/* To be implemented after power control regiter is supported in GELATO production version */
//usleep(2000);
/* Should power down codec to save power here, but R26.3 GELATO cannot dectect plugged-in headset before power up, so we cannot disable codec now even at idle state, will refine this after GELATO improved its headset detection function */
//CODEC_RegSet(0x11, 0x31);
//reg_cache[0xc8].reg_value = 0x31;
}
static void CODEC_Reset(void) {
LOGI(CODEC_Reset, "%s: reset gelato component", __FUNCTION__);
}
static void CODEC_GetTypeAndID(int *type, unsigned char *id) {
LOGI(CODEC_GetTypeAndID, "%s: Get type and ID for gelato component", __FUNCTION__);
*type = COMPONENT_TYPE_CODEC;
//CODEC_RegGet(CODEC_ID_REG, id);
*id = 0x0;
}
static ACH_ReturnCode CODEC_Handle(void *setting) {
ACH_ComponentParameter *param = (ACH_ComponentParameter *)setting;
unsigned short i = 0;
unsigned char numid, value, cache_idx;
if (param && param->i2c.reg_value) {
numid = CODEC_GetRegNumId(param->i2c.reg_index);
if (numid > 0) {
if (param->i2c.length == 1) {
//compare with register cache
cache_idx = numid - REG_NUMID_BASE;
//value = (reg_cache[cache_idx].reg_value & ~param->i2c.reg_mask) | (*param->i2c.reg_value);
//if ((reg_cache[cache_idx].reg_value & param->i2c.reg_mask) != (*param->i2c.reg_value)) {
value = (((*param->i2c.reg_value) << param->i2c.reg_shift) & param->i2c.reg_mask);
if ((reg_cache[cache_idx].reg_value & param->i2c.reg_mask) != value) {
#ifdef DEBUG_CODEC
LOGI(CODEC_Handle, "%s: set register 0x%02x [0x%02x --> 0x%02x]",
__FUNCTION__,
param->i2c.reg_index,
reg_cache[cache_idx].reg_value,
(reg_cache[cache_idx].reg_value & ~param->i2c.reg_mask) | value);
#endif
//reg_cache[cache_idx].reg_value = value;
reg_cache[cache_idx].reg_value = (reg_cache[cache_idx].reg_value & ~param->i2c.reg_mask) | value;
CODEC_RegSet(numid, reg_cache[cache_idx].reg_value);
} else {
#ifdef DEBUG_CODEC
LOGI(CODEC_Handle1, "%s: set register 0x%02x [0x%02x --> 0x%02x] (unchanged)",
__FUNCTION__,
param->i2c.reg_index,
reg_cache[cache_idx].reg_value,
reg_cache[cache_idx].reg_value);
#endif
}
} else if (param->i2c.length > 1) {
//FIXME current no register cache is provided for mixer/eq configuration
#ifdef DEBUG_CODEC
LOGI(CODEC_Handle2, "%s: set register 0x%02x values:", __FUNCTION__, param->i2c.reg_index);
for (i = 0; i < param->i2c.length; i++) {
LOGI(CODEC_Handle3, "%s: \t\t\t -->0x%02x", __FUNCTION__, param->i2c.reg_value[i]);
}
#endif
CODEC_RegBurstSet(numid, param->i2c.length, param->i2c.reg_value);
}
}
}
return ACH_RC_OK;
}