blob: 76658be31424d5e6fd9112eb20133ee80b4c8e0e [file] [log] [blame]
/*
* tfa98xx.c tfa98xx codec module
*
* Copyright (c) 2015 NXP Semiconductors
*
* Author: Sebastien Jan <sjan@baylibre.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.
*/
#define pr_fmt(fmt) "%s(): " fmt, __func__
#include <linux/module.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/debugfs.h>
#include <linux/version.h>
#include <linux/input.h>
#include "config.h"
#define I2C_RETRIES 50
#define I2C_RETRY_DELAY 5 /* ms */
/* TODO: remove genregs usage? */
#ifdef N1A
#include "tfa98xx_genregs_N1A12.h"
#else
#include "tfa98xx_genregs_N1C.h"
#endif
#include "tfa9891_genregs.h"
#include "tfa98xx_tfafieldnames.h"
#include "tfa_internal.h"
#include "tfa.h"
#include "tfa_service.h"
#include "tfa_container.h"
#include "tfa98xx_parameters.h"
#define TFA98XX_VERSION "2.10.2"
/* Change volume selection behavior:
* Uncomment following line to generate a profile change when updating
* a volume control (also changes to the profile of the modified volume
* control)
*/
/*#define TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL 1
*/
/* Supported rates and data formats */
#define TFA98XX_RATES SNDRV_PCM_RATE_8000_48000
/*#define TFA98XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) */
#define TFA98XX_FORMATS SNDRV_PCM_FMTBIT_S16_LE
#define TF98XX_MAX_DSP_START_TRY_COUNT 10
#define XMEM_TAP_ACK 0x0122
#define XMEM_TAP_READ 0x010f
static LIST_HEAD(profile_list); /* list of user selectable profiles */
static int tfa98xx_kmsg_regs = 0;
static int tfa98xx_ftrace_regs = 0;
static struct tfa98xx *tfa98xx_devices[4] = {NULL, NULL, NULL, NULL};
static int tfa98xx_registered_handles = 0;
static int tfa98xx_vsteps[4]={0,0,0,0};
static int tfa98xx_profile = 0; /* store profile */
static int tfa98xx_prof_vsteps[10] = {0}; /* store vstep per profile (single device) */
static int tfa98xx_mixer_profiles = 0; /* number of user selectable profiles */
static int tfa98xx_mixer_profile = 0; /* current mixer profile */
static char *dflt_prof_name = "";
module_param(dflt_prof_name, charp, S_IRUGO);
static int no_start = 0;
module_param(no_start, int, S_IRUGO);
MODULE_PARM_DESC(no_start, "do not start the work queue; for debugging via user\n");
static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx);
static void tfa98xx_interrupt_restore(struct tfa98xx *tfa98xx);
static int tfa98xx_get_fssel(unsigned int rate);
static int get_profile_from_list(char *buf, int id);
static int get_profile_id_for_sr(int id, unsigned int rate);
struct tfa98xx_rate {
unsigned int rate;
unsigned int fssel;
};
static struct tfa98xx_rate rate_to_fssel[] = {
{ 8000, 0 },
{ 11025, 1 },
{ 12000, 2 },
{ 16000, 3 },
{ 22050, 4 },
{ 24000, 5 },
{ 32000, 6 },
{ 44100, 7 },
{ 48000, 8 },
};
/* Wrapper for tfa start */
static enum tfa_error tfa98xx_tfa_start(struct tfa98xx *tfa98xx, int next_profile, int *vstep)
{
enum tfa_error err;
err = tfa_start(next_profile, vstep);
/* Check and update tap-detection state (in case of profile change) */
tfa98xx_tapdet_check_update(tfa98xx);
/* A cold start erases the configuration, including interrupts setting.
* Restore it if required
*/
tfa98xx_interrupt_restore(tfa98xx);
return err;
}
static int tfa98xx_input_open(struct input_dev *dev)
{
struct tfa98xx *tfa98xx = input_get_drvdata(dev);
dev_dbg(tfa98xx->codec->dev, "opening device file\n");
/* note: open function is called only once by the framework.
* No need to count number of open file instances.
*/
if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) {
dev_dbg(&tfa98xx->i2c->dev,
"DSP not loaded, cannot start tap-detection\n");
return -EIO;
}
/* enable tap-detection service */
tfa98xx->tapdet_open = true;
tfa98xx_tapdet_check_update(tfa98xx);
return 0;
}
static void tfa98xx_input_close(struct input_dev *dev)
{
struct tfa98xx *tfa98xx = input_get_drvdata(dev);
dev_dbg(tfa98xx->codec->dev, "closing device file\n");
/* Note: close function is called if the device is unregistered */
/* disable tap-detection service */
tfa98xx->tapdet_open = false;
tfa98xx_tapdet_check_update(tfa98xx);
}
static int tfa98xx_register_inputdev(struct tfa98xx *tfa98xx)
{
int err;
struct input_dev *input;
input = input_allocate_device();
if (!input) {
dev_err(tfa98xx->codec->dev, "Unable to allocate input device\n");
return -ENOMEM;
}
input->evbit[0] = BIT_MASK(EV_KEY);
input->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);
input->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
input->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
input->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
input->keybit[BIT_WORD(BTN_4)] |= BIT_MASK(BTN_4);
input->keybit[BIT_WORD(BTN_5)] |= BIT_MASK(BTN_5);
input->keybit[BIT_WORD(BTN_6)] |= BIT_MASK(BTN_6);
input->keybit[BIT_WORD(BTN_7)] |= BIT_MASK(BTN_7);
input->keybit[BIT_WORD(BTN_8)] |= BIT_MASK(BTN_8);
input->keybit[BIT_WORD(BTN_9)] |= BIT_MASK(BTN_9);
input->open = tfa98xx_input_open;
input->close = tfa98xx_input_close;
input->name = "tfa98xx-tapdetect";
input->id.bustype = BUS_I2C;
input_set_drvdata(input, tfa98xx);
err = input_register_device(input);
if (err) {
dev_err(tfa98xx->codec->dev, "Unable to register input device\n");
goto err_free_dev;
}
dev_dbg(tfa98xx->codec->dev, "Input device for tap-detection registered: %s\n",
input->name);
tfa98xx->input = input;
return 0;
err_free_dev:
input_free_device(input);
return err;
}
/*
* Check if an input device for tap-detection can and shall be registered.
* Register it if appropriate.
* If already registered, check if still relevant and remove it if necessary.
* unregister: true to request inputdev unregistration.
*/
static void __tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx, bool unregister)
{
bool tap_profile = false;
unsigned int i;
for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) {
if (strstr(tfaContProfileName(tfa98xx->handle, i), ".tap")) {
tap_profile = true;
tfa98xx->tapdet_profiles |= 1 << i;
dev_info(tfa98xx->codec->dev,
"found a tap-detection profile (%d - %s)\n",
i, tfaContProfileName(tfa98xx->handle, i));
}
}
/* Check for device support:
* - at device level
* - at container (profile) level
*/
if (!(tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) ||
!tap_profile ||
unregister) {
/* No input device supported or required */
if (tfa98xx->input) {
input_unregister_device(tfa98xx->input);
tfa98xx->input = NULL;
}
return;
}
/* input device required */
if (tfa98xx->input)
dev_info(tfa98xx->codec->dev, "Input device already registered, skipping\n");
else
tfa98xx_register_inputdev(tfa98xx);
}
static void tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx)
{
__tfa98xx_inputdev_check_register(tfa98xx, false);
}
static void tfa98xx_inputdev_unregister(struct tfa98xx *tfa98xx)
{
__tfa98xx_inputdev_check_register(tfa98xx, true);
}
#ifdef CONFIG_DEBUG_FS
/* OTC reporting
* Returns the MTP0 OTC bit value
*/
static int tfa98xx_dbgfs_otc_get(void *data, u64 *val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
struct tfa98xx_control *otc = &(handles_local[tfa98xx->handle].dev_ops.controls.otc);
enum Tfa98xx_Error err, status;
unsigned short value;
mutex_lock(&tfa98xx->dsp_lock);
status = tfa98xx_open(tfa98xx->handle);
if (status) {
mutex_unlock(&tfa98xx->dsp_lock);
return -EBUSY;
}
err = tfa98xx_get_mtp(tfa98xx->handle, &value);
tfa98xx_close(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
if (otc->deferrable) {
if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) {
pr_err("Unable to check DSP access: %d\n", err);
return -EIO;
} else if (err == Tfa98xx_Error_NoClock) {
if (otc->rd_valid) {
/* read cached value */
*val = otc->rd_value;
pr_debug("Returning cached value of OTC: %llu\n", *val);
} else {
pr_info("OTC value never read!\n");
return -EIO;
}
return 0;
}
}
*val = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK)
>> TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS;
pr_debug("OTC : %d\n", value&1);
if (otc->deferrable) {
otc->rd_value = *val;
otc->rd_valid = true;
}
return 0;
}
static int tfa98xx_dbgfs_otc_set(void *data, u64 val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
struct tfa98xx_control *otc = &(handles_local[tfa98xx->handle].dev_ops.controls.otc);
enum Tfa98xx_Error err, status;
if (val != 0 && val != 1) {
pr_err("Unexpected value %llu\n\n", val);
return -EINVAL;
}
mutex_lock(&tfa98xx->dsp_lock);
status = tfa98xx_open(tfa98xx->handle);
if (status) {
mutex_unlock(&tfa98xx->dsp_lock);
return -EBUSY;
}
err = tfa98xx_set_mtp(tfa98xx->handle,
(val << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS)
& TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK,
TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK);
tfa98xx_close(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
if (otc->deferrable) {
if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) {
pr_err("Unable to check DSP access: %d\n", err);
return -EIO;
} else if (err == Tfa98xx_Error_NoClock) {
/* defer OTC */
otc->wr_value = val;
otc->triggered = true;
pr_debug("Deferring write to OTC (%d)\n", otc->wr_value);
return 0;
}
}
/* deferrable: cache the value for subsequent offline read */
if (otc->deferrable) {
otc->rd_value = val;
otc->rd_valid = true;
}
pr_debug("otc < %llu\n", val);
return 0;
}
static int tfa98xx_dbgfs_mtpex_get(void *data, u64 *val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
enum Tfa98xx_Error err, status;
unsigned short value;
mutex_lock(&tfa98xx->dsp_lock);
status = tfa98xx_open(tfa98xx->handle);
if (status) {
mutex_unlock(&tfa98xx->dsp_lock);
return -EBUSY;
}
err = tfa98xx_get_mtp(tfa98xx->handle, &value);
tfa98xx_close(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
if (err != Tfa98xx_Error_Ok) {
pr_err("Unable to check DSP access: %d\n", err);
return -EIO;
}
*val = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK)
>> TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS;
pr_debug("MTPEX : %d\n", value & 2 >> 1);
return 0;
}
static int tfa98xx_dbgfs_mtpex_set(void *data, u64 val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
struct tfa98xx_control *mtpex = &(handles_local[tfa98xx->handle].dev_ops.controls.mtpex);
enum Tfa98xx_Error err, status;
if (val != 0) {
pr_err("Can only clear MTPEX (0 value expected)\n");
return -EINVAL;
}
mutex_lock(&tfa98xx->dsp_lock);
status = tfa98xx_open(tfa98xx->handle);
if (status) {
mutex_unlock(&tfa98xx->dsp_lock);
return -EBUSY;
}
err = tfa98xx_set_mtp(tfa98xx->handle, 0,
TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK);
tfa98xx_close(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
if (mtpex->deferrable) {
if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) {
pr_err("Unable to check DSP access: %d\n", err);
return -EIO;
} else if (err == Tfa98xx_Error_NoClock) {
/* defer OTC */
mtpex->wr_value = 0;
mtpex->triggered = true;
pr_debug("Deferring write to MTPEX (%d)\n", mtpex->wr_value);
return 0;
}
}
pr_debug("mtpex < 0\n");
return 0;
}
static int tfa98xx_dbgfs_temp_get(void *data, u64 *val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
enum Tfa98xx_Error status;
mutex_lock(&tfa98xx->dsp_lock);
status = tfa98xx_open(tfa98xx->handle);
if (status) {
mutex_unlock(&tfa98xx->dsp_lock);
return -EBUSY;
}
*val = tfa98xx_get_exttemp(tfa98xx->handle);
tfa98xx_close(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
return 0;
}
static int tfa98xx_dbgfs_temp_set(void *data, u64 val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
enum Tfa98xx_Error status;
mutex_lock(&tfa98xx->dsp_lock);
status = tfa98xx_open(tfa98xx->handle);
if (status) {
mutex_unlock(&tfa98xx->dsp_lock);
return -EBUSY;
}
tfa98xx_set_exttemp(tfa98xx->handle, (short)val);
tfa98xx_close(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
return 0;
}
/*
* calibration:
* write key phrase to the 'calibration' file to trigger a new calibration
* read the calibration file once to get the calibration result
*/
/* tfa98xx_deferred_calibration_status - called from tfaRunWaitCalibration */
void tfa98xx_deferred_calibration_status(Tfa98xx_handle_t handle, int calibrateDone)
{
struct tfa98xx *tfa98xx = tfa98xx_devices[handle];
struct tfa98xx_control *calib = &(handles_local[handle].dev_ops.controls.calib);
if (calib->wr_value) {
/* a calibration was programmed from the calibration file
* interface
*/
switch (calibrateDone) {
case 1:
/* calibration complete ! */
calib->wr_value = false; /* calibration over */
calib->rd_valid = true; /* result available */
calib->rd_value = true; /* result valid */
tfa_dsp_get_calibration_impedance(tfa98xx->handle);
wake_up_interruptible(&tfa98xx->wq);
break;
case 0:
pr_info("Calibration not complete, still waiting...\n");
break;
case -1:
pr_info("Calibration failed\n");
calib->wr_value = false; /* calibration over */
calib->rd_valid = true; /* result available */
calib->rd_value = false; /* result not valid */
wake_up_interruptible(&tfa98xx->wq);
break;
default:
pr_info("Unknown calibration status: %d\n",
calibrateDone);
}
}
}
static ssize_t tfa98xx_dbgfs_start_get(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
struct i2c_client *i2c = file->private_data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
struct tfa98xx_control *calib = &(handles_local[tfa98xx->handle].dev_ops.controls.calib);
char *str;
int ret;
ret = wait_event_interruptible(tfa98xx->wq, calib->wr_value == false);
if (ret == -ERESTARTSYS) {
/* interrupted by signal */
return ret;
}
if (!calib->rd_valid)
/* no calibration result available - skip */
return 0;
if (calib->rd_value) {
/* Calibration already complete, return result */
str = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!str)
return -ENOMEM;
ret = print_calibration(tfa98xx->handle, str, PAGE_SIZE);
if (ret < 0) {
kfree(str);
return ret;
}
ret = simple_read_from_buffer(user_buf, count, ppos, str, ret);
pr_debug("%s", str);
kfree(str);
calib->rd_value = false;
} else {
/* Calibration failed, return the error code */
const char estr[] = "-1\n";
ret = copy_to_user(user_buf, estr, sizeof(estr));
if (ret)
return -EFAULT;
ret = sizeof(estr);
}
calib->rd_valid = false;
return ret;
}
static ssize_t tfa98xx_dbgfs_start_set(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct i2c_client *i2c = file->private_data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
struct tfa98xx_control *calib = &(handles_local[tfa98xx->handle].dev_ops.controls.calib);
enum Tfa98xx_Error ret;
char buf[32];
const char ref[] = "please calibrate now";
int buf_size;
/* check string length, and account for eol */
if (count > sizeof(ref) + 1 || count < (sizeof(ref) - 1))
return -EINVAL;
buf_size = min(count, (size_t)(sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
/* Compare string, excluding the trailing \0 and the potentials eol */
if (strncmp(buf, ref, sizeof(ref) - 1))
return -EINVAL;
/* Do not open/close tfa98xx: not required by tfa_clibrate */
mutex_lock(&tfa98xx->dsp_lock);
ret = tfa_calibrate(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
if(ret) {
pr_info("Calibration start failed (%d), deferring...\n", ret);
calib->triggered = true;
} else {
pr_info("Calibration started\n");
}
calib->wr_value = true; /* request was triggered from here */
calib->rd_valid = false; /* result not available */
calib->rd_value = false; /* result not valid (dafault) */
return count;
}
static ssize_t tfa98xx_dbgfs_r_read(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
struct i2c_client *i2c = file->private_data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
char *str;
uint16_t status;
int ret, calibrate_done;
mutex_lock(&tfa98xx->dsp_lock);
ret = tfa98xx_open(tfa98xx->handle);
if (ret) {
mutex_unlock(&tfa98xx->dsp_lock);
return -EBUSY;
}
/* Need to ensure DSP is access-able, use mtp read access for this
* purpose
*/
ret = tfa98xx_get_mtp(tfa98xx->handle, &status);
if (ret) {
ret = -EIO;
goto r_c_err;
}
ret = tfaRunWaitCalibration(tfa98xx->handle, &calibrate_done);
if (ret) {
ret = -EIO;
goto r_c_err;
}
str = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!str) {
ret = -ENOMEM;
goto r_c_err;
}
switch (calibrate_done) {
case 1:
/* calibration complete ! */
tfa_dsp_get_calibration_impedance(tfa98xx->handle);
ret = print_calibration(tfa98xx->handle, str, PAGE_SIZE);
break;
case 0:
case -1:
ret = scnprintf(str, PAGE_SIZE, "%d\n", calibrate_done);
break;
default:
pr_err("Unknown calibration status: %d\n", calibrate_done);
ret = -EINVAL;
}
pr_debug("calib_done: %d - ret = %d - %s", calibrate_done, ret, str);
if (ret < 0)
goto r_err;
ret = simple_read_from_buffer(user_buf, count, ppos, str, ret);
r_err:
kfree(str);
r_c_err:
tfa98xx_close(tfa98xx->handle);
mutex_unlock(&tfa98xx->dsp_lock);
return ret;
}
static ssize_t tfa98xx_dbgfs_version_read(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
char str[] = TFA98XX_VERSION "\n";
int ret;
ret = simple_read_from_buffer(user_buf, count, ppos, str, sizeof(str));
return ret;
}
static ssize_t tfa98xx_dbgfs_dsp_state_get(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
struct i2c_client *i2c = file->private_data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
int ret = 0;
char *str;
switch (tfa98xx->dsp_init) {
case TFA98XX_DSP_INIT_STOPPED:
str = "Stopped\n";
break;
case TFA98XX_DSP_INIT_RECOVER:
str = "Recover requested\n";
break;
case TFA98XX_DSP_INIT_FAIL:
str = "Failed init\n";
break;
case TFA98XX_DSP_INIT_PENDING:
str = "Pending init\n";
break;
case TFA98XX_DSP_INIT_DONE:
str = "Init complete\n";
break;
default:
str = "Invalid\n";
}
ret = simple_read_from_buffer(user_buf, count, ppos, str, strlen(str));
return ret;
}
static ssize_t tfa98xx_dbgfs_dsp_state_set(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct i2c_client *i2c = file->private_data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
enum tfa_error ret;
char buf[32];
const char start_cmd[] = "start";
const char stop_cmd[] = "stop";
const char mon_start_cmd[] = "monitor start";
const char mon_stop_cmd[] = "monitor stop";
int buf_size;
buf_size = min(count, (size_t)(sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
/* Compare strings, excluding the trailing \0 */
if (!strncmp(buf, start_cmd, sizeof(start_cmd) - 1)) {
pr_info("Manual triggering of dsp start...\n");
mutex_lock(&tfa98xx->dsp_lock);
ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps);
mutex_unlock(&tfa98xx->dsp_lock);
pr_debug("tfa_start complete: %d\n", ret);
} else if (!strncmp(buf, stop_cmd, sizeof(stop_cmd) - 1)) {
pr_info("Manual triggering of dsp stop...\n");
mutex_lock(&tfa98xx->dsp_lock);
ret = tfa_stop();
mutex_unlock(&tfa98xx->dsp_lock);
pr_debug("tfa_stop complete: %d\n", ret);
} else if (!strncmp(buf, mon_start_cmd, sizeof(mon_start_cmd) - 1)) {
pr_info("Manual start of monitor thread...\n");
queue_delayed_work(tfa98xx->tfa98xx_wq,
&tfa98xx->monitor_work, HZ);
} else if (!strncmp(buf, mon_stop_cmd, sizeof(mon_stop_cmd) - 1)) {
pr_info("Manual stop of monitor thread...\n");
cancel_delayed_work_sync(&tfa98xx->monitor_work);
} else {
return -EINVAL;
}
return count;
}
static ssize_t tfa98xx_dbgfs_accounting_get(struct file *file,
char __user *user_buf, size_t count,
loff_t *ppos)
{
struct i2c_client *i2c = file->private_data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
char str[255];
int ret;
int n = 0;
n += snprintf(&str[n], sizeof(str)-1-n, "Wait4Src\t= %d\n", tfa98xx->count_wait_for_source_state);
n += snprintf(&str[n], sizeof(str)-1-n, "NOCLK\t\t= %d\n", tfa98xx->count_noclk);
str[n+1] = '\0'; /* in case str is not large enough */
ret = simple_read_from_buffer(user_buf, count, ppos, str, n+1);
return ret;
}
static int tfa98xx_dbgfs_pga_gain_get(void *data, u64 *val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
int err;
unsigned int value;
/* *val = TFA_GET_BF(tfa98xx->handle, SAAMGAIN);*/
err = regmap_read(tfa98xx->regmap, TFA98XX_CTRL_SAAM_PGA, &value);
*val = (value & TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK) >>
TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS;
return 0;
}
static int tfa98xx_dbgfs_pga_gain_set(void *data, u64 val)
{
struct i2c_client *i2c = (struct i2c_client *)data;
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
int err;
unsigned int value;
value = val & 0xffff;
if (value > 7)
return -EINVAL;
/* TFA_SET_BF(tfa98xx->handle, SAAMGAIN, value);*/
err = regmap_update_bits(tfa98xx->regmap, TFA98XX_CTRL_SAAM_PGA,
TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK,
value << TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS);
return err;
}
/* Direct registers access - provide register address in hex */
#define TFA98XX_DEBUGFS_REG_SET(__reg) \
static int tfa98xx_dbgfs_reg_##__reg##_set(void *data, u64 val) \
{ \
struct i2c_client *i2c = (struct i2c_client *)data; \
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); \
unsigned int ret, value; \
\
ret = regmap_write(tfa98xx->regmap, 0x##__reg, (val & 0xffff)); \
value = val & 0xffff; \
return 0; \
} \
static int tfa98xx_dbgfs_reg_##__reg##_get(void *data, u64 *val) \
{ \
struct i2c_client *i2c = (struct i2c_client *)data; \
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); \
unsigned int value; \
int ret; \
ret = regmap_read(tfa98xx->regmap, 0x##__reg, &value); \
*val = value; \
return 0; \
} \
DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_reg_##__reg##_fops, tfa98xx_dbgfs_reg_##__reg##_get, \
tfa98xx_dbgfs_reg_##__reg##_set, "0x%llx\n");
#define VAL(str) #str
#define TOSTRING(str) VAL(str)
#define TFA98XX_DEBUGFS_REG_CREATE_FILE(__reg, __name) \
debugfs_create_file(TOSTRING(__reg) "-" TOSTRING(__name), S_IRUGO|S_IWUGO, dbg_reg_dir,\
i2c, &tfa98xx_dbgfs_reg_##__reg##_fops);
TFA98XX_DEBUGFS_REG_SET(00);
TFA98XX_DEBUGFS_REG_SET(01);
TFA98XX_DEBUGFS_REG_SET(02);
TFA98XX_DEBUGFS_REG_SET(03);
TFA98XX_DEBUGFS_REG_SET(04);
TFA98XX_DEBUGFS_REG_SET(05);
TFA98XX_DEBUGFS_REG_SET(06);
TFA98XX_DEBUGFS_REG_SET(07);
TFA98XX_DEBUGFS_REG_SET(08);
TFA98XX_DEBUGFS_REG_SET(09);
TFA98XX_DEBUGFS_REG_SET(0A);
TFA98XX_DEBUGFS_REG_SET(0B);
TFA98XX_DEBUGFS_REG_SET(0F);
TFA98XX_DEBUGFS_REG_SET(10);
TFA98XX_DEBUGFS_REG_SET(11);
TFA98XX_DEBUGFS_REG_SET(12);
TFA98XX_DEBUGFS_REG_SET(13);
TFA98XX_DEBUGFS_REG_SET(22);
TFA98XX_DEBUGFS_REG_SET(25);
DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_otc_fops, tfa98xx_dbgfs_otc_get,
tfa98xx_dbgfs_otc_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_mtpex_fops, tfa98xx_dbgfs_mtpex_get,
tfa98xx_dbgfs_mtpex_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_temp_fops, tfa98xx_dbgfs_temp_get,
tfa98xx_dbgfs_temp_set, "%llu\n");
DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_pga_gain_fops, tfa98xx_dbgfs_pga_gain_get,
tfa98xx_dbgfs_pga_gain_set, "%llu\n");
static const struct file_operations tfa98xx_dbgfs_calib_start_fops = {
.open = simple_open,
.read = tfa98xx_dbgfs_start_get,
.write = tfa98xx_dbgfs_start_set,
.llseek = default_llseek,
};
static const struct file_operations tfa98xx_dbgfs_r_fops = {
.open = simple_open,
.read = tfa98xx_dbgfs_r_read,
.llseek = default_llseek,
};
static const struct file_operations tfa98xx_dbgfs_version_fops = {
.open = simple_open,
.read = tfa98xx_dbgfs_version_read,
.llseek = default_llseek,
};
static const struct file_operations tfa98xx_dbgfs_dsp_state_fops = {
.open = simple_open,
.read = tfa98xx_dbgfs_dsp_state_get,
.write = tfa98xx_dbgfs_dsp_state_set,
.llseek = default_llseek,
};
static const struct file_operations tfa98xx_dbgfs_accounting_fops = {
.open = simple_open,
.read = tfa98xx_dbgfs_accounting_get,
.llseek = default_llseek,
};
static void tfa98xx_debug_init(struct tfa98xx *tfa98xx, struct i2c_client *i2c)
{
char name[50];
struct dentry *dbg_reg_dir;
scnprintf(name, MAX_CONTROL_NAME, "%s-%x", i2c->name, i2c->addr);
tfa98xx->dbg_dir = debugfs_create_dir(name, NULL);
debugfs_create_file("OTC", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_calib_otc_fops);
debugfs_create_file("MTPEX", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_calib_mtpex_fops);
debugfs_create_file("TEMP", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_calib_temp_fops);
debugfs_create_file("calibrate", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_calib_start_fops);
debugfs_create_file("R", S_IRUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_r_fops);
debugfs_create_file("version", S_IRUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_version_fops);
debugfs_create_file("dsp-state", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_dsp_state_fops);
debugfs_create_file("accounting", S_IRUGO, tfa98xx->dbg_dir,
i2c, &tfa98xx_dbgfs_accounting_fops);
/* Direct registers access */
if (tfa98xx->flags & TFA98XX_FLAG_TFA9890_FAM_DEV) {
dbg_reg_dir = debugfs_create_dir("regs", tfa98xx->dbg_dir);
TFA98XX_DEBUGFS_REG_CREATE_FILE(00, STATUS);
TFA98XX_DEBUGFS_REG_CREATE_FILE(01, BATTERYVOLTAGE);
TFA98XX_DEBUGFS_REG_CREATE_FILE(02, TEMPERATURE);
TFA98XX_DEBUGFS_REG_CREATE_FILE(03, REVISIONNUMBER);
TFA98XX_DEBUGFS_REG_CREATE_FILE(04, I2SREG);
TFA98XX_DEBUGFS_REG_CREATE_FILE(05, BAT_PROT);
TFA98XX_DEBUGFS_REG_CREATE_FILE(06, AUDIO_CTR);
TFA98XX_DEBUGFS_REG_CREATE_FILE(07, DCDCBOOST);
TFA98XX_DEBUGFS_REG_CREATE_FILE(08, SPKR_CALIBRATION);
TFA98XX_DEBUGFS_REG_CREATE_FILE(09, SYS_CTRL);
TFA98XX_DEBUGFS_REG_CREATE_FILE(0A, I2S_SEL_REG);
TFA98XX_DEBUGFS_REG_CREATE_FILE(0B, HIDDEN_MTP_KEY2);
TFA98XX_DEBUGFS_REG_CREATE_FILE(0F, INTERRUPT_REG);
TFA98XX_DEBUGFS_REG_CREATE_FILE(10, PDM_CTRL);
TFA98XX_DEBUGFS_REG_CREATE_FILE(11, PDM_OUT_CTRL);
TFA98XX_DEBUGFS_REG_CREATE_FILE(12, PDM_DS4_R);
TFA98XX_DEBUGFS_REG_CREATE_FILE(13, PDM_DS4_L);
TFA98XX_DEBUGFS_REG_CREATE_FILE(22, CTRL_SAAM_PGA);
TFA98XX_DEBUGFS_REG_CREATE_FILE(25, MISC_CTRL);
}
if (tfa98xx->flags & TFA98XX_FLAG_TFA9897_FAM_DEV) {
dbg_reg_dir = debugfs_create_dir("regs", tfa98xx->dbg_dir);
TFA98XX_DEBUGFS_REG_CREATE_FILE(00, STATUS);
TFA98XX_DEBUGFS_REG_CREATE_FILE(01, BATTERYVOLTAGE);
TFA98XX_DEBUGFS_REG_CREATE_FILE(02, TEMPERATURE);
TFA98XX_DEBUGFS_REG_CREATE_FILE(03, REVISIONNUMBER);
TFA98XX_DEBUGFS_REG_CREATE_FILE(04, I2SREG);
TFA98XX_DEBUGFS_REG_CREATE_FILE(05, BAT_PROT);
TFA98XX_DEBUGFS_REG_CREATE_FILE(06, AUDIO_CTR);
TFA98XX_DEBUGFS_REG_CREATE_FILE(07, DCDCBOOST);
TFA98XX_DEBUGFS_REG_CREATE_FILE(08, SPKR_CALIBRATION);
TFA98XX_DEBUGFS_REG_CREATE_FILE(09, SYS_CTRL);
}
if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) {
dev_dbg(tfa98xx->dev, "Adding pga_gain debug interface\n");
debugfs_create_file("pga_gain", S_IRUGO, tfa98xx->dbg_dir,
tfa98xx->i2c,
&tfa98xx_dbgfs_pga_gain_fops);
}
}
static void tfa98xx_debug_remove(struct tfa98xx *tfa98xx)
{
if (tfa98xx->dbg_dir)
debugfs_remove_recursive(tfa98xx->dbg_dir);
}
#endif
static int tfa98xx_get_vstep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
int mixer_profile = kcontrol->private_value;
int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate);
int vstep = tfa98xx_prof_vsteps[profile];
ucontrol->value.integer.value[0] =
tfacont_get_max_vstep(tfa98xx->handle, profile)
- vstep - 1;
return 0;
}
static int tfa98xx_set_vstep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
int mixer_profile = kcontrol->private_value;
int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate);
int value = ucontrol->value.integer.value[0];
int vstep = tfa98xx_prof_vsteps[profile];
int vsteps = tfacont_get_max_vstep(tfa98xx->handle, profile);
int new_vstep, err = 0;
int ready = 0;
unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8;
if (no_start != 0)
return 0;
if (vstep == vsteps - value - 1)
return 0;
new_vstep = vsteps - value - 1;
if (new_vstep < 0)
new_vstep = 0;
tfa98xx_prof_vsteps[profile] = new_vstep;
#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL
if (profile == tfa98xx_profile) {
#endif
/* this is the active profile, program the new vstep */
tfa98xx_vsteps[0] = new_vstep;
tfa98xx_vsteps[1] = new_vstep;
mutex_lock(&tfa98xx->dsp_lock);
tfa98xx_open(tfa98xx->handle);
tfa98xx_dsp_system_stable(tfa98xx->handle, &ready);
tfa98xx_close(tfa98xx->handle);
/* Enable internal clk (osc1m) to switch profile */
if ((tfa98xx_dev_family(tfa98xx->handle) == 2) && (ready == 0)) {
/* Disable interrupts (Enabled again in the wrapper function: tfa98xx_tfa_start) */
regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0);
/* Set polarity to high */
TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1);
TFA_SET_BF(tfa98xx->handle, RST, 1);
TFA_SET_BF(tfa98xx->handle, SBSL, 0);
TFA_SET_BF(tfa98xx->handle, AMPC, 0);
TFA_SET_BF(tfa98xx->handle, AMPE, 0);
TFA_SET_BF(tfa98xx->handle, REFCKSEL, 1);
ready = 1;
}
if (ready) {
err = tfa98xx_tfa_start(tfa98xx, profile, tfa98xx_vsteps);
if (err) {
pr_err("Write vstep error: %d\n", err);
} else {
pr_debug("Succesfully changed vstep index!\n");
}
}
if (tfa98xx_dev_family(tfa98xx->handle) == 2) {
/* Set back to external clock */
TFA_SET_BF(tfa98xx->handle, REFCKSEL, 0);
TFA_SET_BF(tfa98xx->handle, SBSL, 1);
}
mutex_unlock(&tfa98xx->dsp_lock);
#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL
}
#endif
pr_debug("vstep:%d, (control value: %d) - profile %d\n", new_vstep,
value, profile);
return (err == 0);
}
static int tfa98xx_info_vstep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
int mixer_profile = kcontrol->private_value;
int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;// TODO handles_local[dev_idx].spkr_count
uinfo->value.integer.min = 0;
uinfo->value.integer.max = tfacont_get_max_vstep(tfa98xx->handle, profile) - 1;
pr_debug("vsteps count: %d [prof=%d]\n", tfacont_get_max_vstep(tfa98xx->handle, profile),
profile);
return 0;
}
static int tfa98xx_get_profile(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = tfa98xx_mixer_profile;
return 0;
}
static int tfa98xx_set_profile(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8;
int profile_count = tfa98xx_mixer_profiles;
int profile = tfa98xx_mixer_profile;
int new_profile = ucontrol->value.integer.value[0];
int err;
int ready = 0;
int prof_idx;
if (no_start != 0)
return 0;
if (new_profile == profile)
return 0;
if (new_profile >= profile_count)
return 0;
/* get the container profile for the requested sample rate */
prof_idx = get_profile_id_for_sr(new_profile, tfa98xx->rate);
if (prof_idx < 0) {
pr_err("tfa98xx: sample rate [%d] not supported for this mixer profile [%d].\n", tfa98xx->rate, new_profile);
return 0;
}
pr_debug("selected container profile [%d]\n", prof_idx);
/* update mixer profile */
tfa98xx_mixer_profile = new_profile;
/* update 'real' profile (container profile) */
tfa98xx_profile = prof_idx;
tfa98xx_vsteps[0] = tfa98xx_prof_vsteps[prof_idx];
tfa98xx_vsteps[1] = tfa98xx_prof_vsteps[prof_idx];
/*
* Don't call tfa_start() on TFA1 if there is no clock.
* For TFA2 is able to load the profile without clock.
*/
mutex_lock(&tfa98xx->dsp_lock);
tfa98xx_open(tfa98xx->handle);
tfa98xx_dsp_system_stable(tfa98xx->handle, &ready);
tfa98xx_close(tfa98xx->handle);
/* Enable internal clk (osc1m) to switch profile */
if (tfa98xx_dev_family(tfa98xx->handle) == 2 && ready == 0) {
/* Disable interrupts (Enabled again in the wrapper function: tfa98xx_tfa_start) */
regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0);
/* Set polarity to high */
TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1);
TFA_SET_BF(tfa98xx->handle, RST, 1);
TFA_SET_BF_VOLATILE(tfa98xx->handle, SBSL, 0);
TFA_SET_BF(tfa98xx->handle, AMPC, 0);
TFA_SET_BF(tfa98xx->handle, AMPE, 0);
TFA_SET_BF(tfa98xx->handle, REFCKSEL, 1);
ready = 1;
}
if (ready) {
/* Also re-enables the interrupts */
err = tfa98xx_tfa_start(tfa98xx, prof_idx, tfa98xx_vsteps);
if (err) {
pr_info("Write profile error: %d\n", err);
} else {
pr_debug("Changed to profile %d (vstep = %d)\n", prof_idx,
tfa98xx_vsteps[0]);
}
}
if (tfa98xx_dev_family(tfa98xx->handle) == 2) {
/* Set back to external clock */
TFA_SET_BF(tfa98xx->handle, REFCKSEL, 0);
TFA_SET_BF_VOLATILE(tfa98xx->handle, SBSL, 1);
}
mutex_unlock(&tfa98xx->dsp_lock);
/* Flag DSP as invalidated as the profile change may invalidate the
* current DSP configuration. That way, further stream start can
* trigger a tfa_start.
*/
tfa98xx->dsp_init = TFA98XX_DSP_INIT_INVALIDATED;
return 1;
}
static struct snd_kcontrol_new *tfa98xx_controls;
/* copies the profile basename (i.e. part until .) into buf */
static void get_profile_basename(char* buf, char* profile)
{
int cp_len = 0, idx = 0;
char *pch;
pch = strchr(profile, '.');
idx = pch - profile;
cp_len = (pch != NULL) ? idx : (int) strlen(profile);
memcpy(buf, profile, cp_len);
buf[cp_len] = 0;
}
/* return the profile name accociated with id from the profile list */
static int get_profile_from_list(char *buf, int id)
{
struct tfa98xx_baseprofile *bprof;
list_for_each_entry(bprof, &profile_list, list) {
if (bprof->item_id == id) {
strcpy(buf, bprof->basename);
return 0;
}
}
return -1;
}
/* search for the profile in the profile list */
static int is_profile_in_list(char *profile, int len)
{
struct tfa98xx_baseprofile *bprof;
list_for_each_entry(bprof, &profile_list, list) {
if (0 == strncmp(bprof->basename, profile, len))
return 1;
}
return 0;
}
/*
* for the profile with id, look if the requested samplerate is
* supported, if found return the (container)profile for this
* samplerate, on error or if not found return -1
*/
static int get_profile_id_for_sr(int id, unsigned int rate)
{
int idx = 0;
struct tfa98xx_baseprofile *bprof;
list_for_each_entry(bprof, &profile_list, list) {
if (id == bprof->item_id) {
idx = tfa98xx_get_fssel(rate);
if (idx < 0) {
/* samplerate not supported */
return -1;
}
return bprof->sr_rate_sup[idx];
}
}
/* profile not found */
return -1;
}
/* check if this profile is a calibration profile */
static int is_calibration_profile(char *profile)
{
if (strstr(profile, ".cal") != NULL)
return 1;
return 0;
}
/*
* adds the (container)profile index of the samplerate found in
* the (container)profile to a fixed samplerate table in the (mixer)profile
*/
static int add_sr_to_profile(struct tfa98xx *tfa98xx, char *basename, int len, int profile)
{
struct tfa98xx_baseprofile *bprof;
int idx = 0;
unsigned int sr = 0;
list_for_each_entry(bprof, &profile_list, list) {
if (0 == strncmp(bprof->basename, basename, len)) {
/* add supported samplerate for this profile */
sr = tfa98xx_get_profile_sr(tfa98xx->handle, profile);
if (!sr) {
pr_err("unable to identify supported sample rate for %s\n", bprof->basename);
return -1;
}
/* get the index for this samplerate */
idx = tfa98xx_get_fssel(sr);
if (idx < 0 || idx >= TFA98XX_NUM_RATES) {
pr_err("invalid index for samplerate %d\n", idx);
return -1;
}
/* enter the (container)profile for this samplerate at the corresponding index */
bprof->sr_rate_sup[idx] = profile;
pr_debug("added profile:samplerate = [%d:%d] for mixer profile: %s\n", profile, sr, bprof->basename);
}
}
return 0;
}
static int tfa98xx_info_profile(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
char profile_name[MAX_CONTROL_NAME] = {0};
int count = tfa98xx_mixer_profiles, err = -1;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = count;
if (uinfo->value.enumerated.item >= count)
uinfo->value.enumerated.item = count - 1;
err = get_profile_from_list(profile_name, uinfo->value.enumerated.item);
if (err != 0)
return -EINVAL;
strcpy(uinfo->value.enumerated.name, profile_name);
return 0;
}
static int tfa98xx_get_stop_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = 0;
return 0;
}
static int tfa98xx_set_stop_ctl(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
#else
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
#endif
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
int ready = 0;
pr_debug("%ld\n", ucontrol->value.integer.value[0]);
tfa98xx_open(tfa98xx->handle);
tfa98xx_dsp_system_stable(tfa98xx->handle, &ready);
tfa98xx_close(tfa98xx->handle);
if ((ucontrol->value.integer.value[0] != 0) && ready) {
cancel_delayed_work_sync(&tfa98xx->monitor_work);
cancel_delayed_work_sync(&tfa98xx->init_work);
if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK)
return 0;
mutex_lock(&tfa98xx->dsp_lock);
tfa_stop();
tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED;
mutex_unlock(&tfa98xx->dsp_lock);
}
ucontrol->value.integer.value[0] = 0;
return 1;
}
static int tfa98xx_create_controls(struct tfa98xx *tfa98xx)
{
int prof, nprof, mix_index = 0;
int nr_controls = 0, id = 0;
char *name;
struct tfa98xx_baseprofile *bprofile;
/* Create the following controls:
* - enum control to select the active profile
* - one volume control for each profile hosting a vstep
* - Stop control on TFA1 devices
*/
nr_controls = 1; /* Profile control */
if (tfa98xx_dev_family(tfa98xx->handle) == 1)
nr_controls += 1; /* Stop control */
/* allocate the tfa98xx_controls base on the nr of profiles */
nprof = tfaContMaxProfile(tfa98xx->handle);
for (prof = 0; prof < nprof; prof++) {
if (tfacont_get_max_vstep(tfa98xx->handle, prof))
nr_controls++; /* Playback Volume control */
}
tfa98xx_controls = devm_kzalloc(tfa98xx->codec->dev,
nr_controls * sizeof(tfa98xx_controls[0]), GFP_KERNEL);
if(!tfa98xx_controls)
return -ENOMEM;
/* Create a mixer item for selecting the active profile */
name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL);
if (!name)
return -ENOMEM;
scnprintf(name, MAX_CONTROL_NAME, "%s Profile", tfa98xx->fw.name);
tfa98xx_controls[mix_index].name = name;
tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
tfa98xx_controls[mix_index].info = tfa98xx_info_profile;
tfa98xx_controls[mix_index].get = tfa98xx_get_profile;
tfa98xx_controls[mix_index].put = tfa98xx_set_profile;
// tfa98xx_controls[mix_index].private_value = profs; /* save number of profiles */
mix_index++;
/* create mixer items for each profile that has volume */
for (prof = 0; prof < nprof; prof++) {
/* create an new empty profile */
bprofile = devm_kzalloc(tfa98xx->codec->dev, sizeof(*bprofile), GFP_KERNEL);
if (!bprofile)
return -ENOMEM;
bprofile->len = 0;
bprofile->item_id = -1;
INIT_LIST_HEAD(&bprofile->list);
/* copy profile name into basename until the . */
get_profile_basename(bprofile->basename, tfaContProfileName(tfa98xx->handle, prof));
bprofile->len = strlen(bprofile->basename);
/*
* search the profile list for a profile with basename, if it is not found then
* add it to the list and add a new mixer control (if it has vsteps)
* also, if it is a calibration profile, do not add it to the list
*/
if (is_profile_in_list(bprofile->basename, bprofile->len) == 0 &&
is_calibration_profile(tfaContProfileName(tfa98xx->handle, prof)) == 0) {
/* the profile is not present, add it to the list */
list_add(&bprofile->list, &profile_list);
bprofile->item_id = id++;
pr_debug("profile added [%d]: %s\n", bprofile->item_id, bprofile->basename);
if (tfacont_get_max_vstep(tfa98xx->handle, prof)) {
name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL);
if (!name)
return -ENOMEM;
scnprintf(name, MAX_CONTROL_NAME, "%s %s Playback Volume",
tfa98xx->fw.name, bprofile->basename);
tfa98xx_controls[mix_index].name = name;
tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
tfa98xx_controls[mix_index].info = tfa98xx_info_vstep;
tfa98xx_controls[mix_index].get = tfa98xx_get_vstep;
tfa98xx_controls[mix_index].put = tfa98xx_set_vstep;
tfa98xx_controls[mix_index].private_value = prof; /* save profile index */
mix_index++;
}
}
/* look for the basename profile in the list of mixer profiles and add the
container profile index to the supported samplerates of this mixer profile */
add_sr_to_profile(tfa98xx, bprofile->basename, bprofile->len, prof);
}
if (tfa98xx_dev_family(tfa98xx->handle) == 1) {
/* Create a mixer item for stop control on TFA1 */
name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL);
if (!name)
return -ENOMEM;
scnprintf(name, MAX_CONTROL_NAME, "%s Stop", tfa98xx->fw.name);
tfa98xx_controls[mix_index].name = name;
tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
tfa98xx_controls[mix_index].info = snd_soc_info_bool_ext;
tfa98xx_controls[mix_index].get = tfa98xx_get_stop_ctl;
tfa98xx_controls[mix_index].put = tfa98xx_set_stop_ctl;
mix_index++;
}
/* set the number of user selectable profiles in the mixer */
tfa98xx_mixer_profiles = id;
return snd_soc_add_codec_controls(tfa98xx->codec,
tfa98xx_controls, mix_index);
}
static void *tfa98xx_devm_kstrdup(struct device *dev, char *buf)
{
char *str = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL);
if (!str)
return str;
memcpy(str, buf, strlen(buf));
return str;
}
static int tfa98xx_append_i2c_address(struct device *dev,
struct i2c_client *i2c,
struct snd_soc_dapm_widget *widgets,
int num_widgets,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
char buf[50];
int i;
int i2cbus = i2c->adapter->nr;
int addr = i2c->addr;
if (dai_drv && num_dai > 0)
for(i = 0; i < num_dai; i++) {
snprintf(buf, 50, "%s-%x-%x",dai_drv[i].name, i2cbus,
addr);
dai_drv[i].name = tfa98xx_devm_kstrdup(dev, buf);
snprintf(buf, 50, "%s-%x-%x",
dai_drv[i].playback.stream_name,
i2cbus, addr);
dai_drv[i].playback.stream_name = tfa98xx_devm_kstrdup(dev, buf);
snprintf(buf, 50, "%s-%x-%x",
dai_drv[i].capture.stream_name,
i2cbus, addr);
dai_drv[i].capture.stream_name = tfa98xx_devm_kstrdup(dev, buf);
}
/* the idea behind this is convert:
* SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
* into:
* SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback-2-36", 0, SND_SOC_NOPM, 0, 0),
*/
if (widgets && num_widgets > 0)
for(i = 0; i < num_widgets; i++) {
if(!widgets[i].sname)
continue;
if((widgets[i].id == snd_soc_dapm_aif_in)
|| (widgets[i].id == snd_soc_dapm_aif_out)) {
snprintf(buf, 50, "%s-%x-%x", widgets[i].sname,
i2cbus, addr);
widgets[i].sname = tfa98xx_devm_kstrdup(dev, buf);
}
}
return 0;
}
static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_common[] = {
/* Stream widgets */
SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AIF OUT", "AIF Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_OUTPUT("OUTL"),
SND_SOC_DAPM_INPUT("AEC Loopback"),
};
static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_stereo[] = {
SND_SOC_DAPM_OUTPUT("OUTR"),
};
static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_saam[] = {
SND_SOC_DAPM_INPUT("SAAM MIC"),
};
static struct snd_soc_dapm_widget tfa9888_dapm_inputs[] = {
SND_SOC_DAPM_INPUT("DMIC1"),
SND_SOC_DAPM_INPUT("DMIC2"),
SND_SOC_DAPM_INPUT("DMIC3"),
SND_SOC_DAPM_INPUT("DMIC4"),
};
static const struct snd_soc_dapm_route tfa98xx_dapm_routes_common[] = {
{ "OUTL", NULL, "AIF IN" },
{ "AIF OUT", NULL, "AEC Loopback" },
};
static const struct snd_soc_dapm_route tfa98xx_dapm_routes_saam[] = {
{ "AIF OUT", NULL, "SAAM MIC" },
};
static const struct snd_soc_dapm_route tfa98xx_dapm_routes_stereo[] = {
{ "OUTR", NULL, "AIF IN" },
};
static const struct snd_soc_dapm_route tfa9888_input_dapm_routes[] = {
{ "AIF OUT", NULL, "DMIC1" },
{ "AIF OUT", NULL, "DMIC2" },
{ "AIF OUT", NULL, "DMIC3" },
{ "AIF OUT", NULL, "DMIC4" },
};
static void tfa98xx_add_widgets(struct tfa98xx *tfa98xx)
{
struct snd_soc_dapm_context *dapm = &tfa98xx->codec->dapm;
struct snd_soc_dapm_widget *widgets;
unsigned int num_dapm_widgets = ARRAY_SIZE(tfa98xx_dapm_widgets_common);
widgets = devm_kzalloc(&tfa98xx->i2c->dev,
sizeof(struct snd_soc_dapm_widget) *
ARRAY_SIZE(tfa98xx_dapm_widgets_common),
GFP_KERNEL);
if (!widgets)
return;
memcpy(widgets, tfa98xx_dapm_widgets_common,
sizeof(struct snd_soc_dapm_widget) *
ARRAY_SIZE(tfa98xx_dapm_widgets_common));
#if 0
tfa98xx_append_i2c_address(&tfa98xx->i2c->dev,
tfa98xx->i2c,
widgets,
num_dapm_widgets,
NULL,
0);
#endif
snd_soc_dapm_new_controls(dapm, widgets,
ARRAY_SIZE(tfa98xx_dapm_widgets_common));
snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_common,
ARRAY_SIZE(tfa98xx_dapm_routes_common));
if (tfa98xx->flags & TFA98XX_FLAG_STEREO_DEVICE) {
snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_stereo,
ARRAY_SIZE(tfa98xx_dapm_widgets_stereo));
snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_stereo,
ARRAY_SIZE(tfa98xx_dapm_routes_stereo));
}
if (tfa98xx->flags & TFA98XX_FLAG_MULTI_MIC_INPUTS) {
snd_soc_dapm_new_controls(dapm, tfa9888_dapm_inputs,
ARRAY_SIZE(tfa9888_dapm_inputs));
snd_soc_dapm_add_routes(dapm, tfa9888_input_dapm_routes,
ARRAY_SIZE(tfa9888_input_dapm_routes));
}
if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) {
snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_saam,
ARRAY_SIZE(tfa98xx_dapm_widgets_saam));
snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_saam,
ARRAY_SIZE(tfa98xx_dapm_routes_saam));
}
}
/* Match tfa98xx device structure with a valid DSP handle */
/* TODO can be removed once we pass the device struct in stead of handles
The check in tfa98xx_register_dsp() is implicitly done in tfa_probe() /tfa98xx_cnt_slave2idx(_)
*/
static int tfa98xx_register_dsp(struct tfa98xx *tfa98xx)
{
int i, handle = -1;
u8 slave;
for (i = 0; i < tfa98xx_cnt_max_device(); i++) {
if (tfaContGetSlave(i, &slave) != Tfa98xx_Error_Ok)
goto reg_err;
pr_debug("%s: i=%d - dev = 0x%x\n", __func__, i, slave);
if (slave == tfa98xx->i2c->addr) {
handle = i;
break;
}
}
if (handle != -1) {
tfa98xx_devices[handle] = tfa98xx;
dev_info(&tfa98xx->i2c->dev,
"Registered DSP instance with handle %d\n",
handle);
tfa98xx_registered_handles++;
return handle;
}
reg_err:
dev_err(&tfa98xx->i2c->dev,
"Unable to match I2C address 0x%x with a container device\n",
tfa98xx->i2c->addr);
return -EINVAL;
}
static void tfa98xx_unregister_dsp(struct tfa98xx *tfa98xx)
{
tfa98xx_registered_handles--;
tfa98xx_devices[tfa98xx->handle] = NULL;
dev_info(&tfa98xx->i2c->dev, "Un-registered DSP instance with handle %d\n",
tfa98xx->handle);
}
/* I2C wrapper functions */
enum Tfa98xx_Error tfa98xx_write_register16(Tfa98xx_handle_t handle,
unsigned char subaddress,
unsigned short value)
{
enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
struct tfa98xx *tfa98xx;
int ret;
int retries = I2C_RETRIES;
if (tfa98xx_devices[handle]) {
tfa98xx = tfa98xx_devices[handle];
if (!tfa98xx || !tfa98xx->regmap) {
pr_err("No tfa98xx regmap available\n");
return Tfa98xx_Error_Bad_Parameter;
}
retry:
ret = regmap_write(tfa98xx->regmap, subaddress, value);
if (ret < 0) {
pr_warn("i2c error, retries left: %d\n", retries);
if (retries) {
retries--;
msleep(I2C_RETRY_DELAY);
goto retry;
}
return Tfa98xx_Error_Fail;
}
if (tfa98xx_kmsg_regs)
dev_dbg(&tfa98xx->i2c->dev, " WR reg=0x%02x, val=0x%04x %s\n",
subaddress, value,
ret<0? "Error!!" : "");
if(tfa98xx_ftrace_regs)
tfa98xx_trace_printk("\tWR reg=0x%02x, val=0x%04x %s\n",
subaddress, value,
ret<0? "Error!!" : "");
} else {
pr_err("No device available\n");
error = Tfa98xx_Error_Fail;
}
return error;
}
enum Tfa98xx_Error tfa98xx_read_register16(Tfa98xx_handle_t handle,
unsigned char subaddress,
unsigned short *val)
{
enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
struct tfa98xx *tfa98xx;
unsigned int value;
int retries = I2C_RETRIES;
int ret;
if (tfa98xx_devices[handle]) {
tfa98xx = tfa98xx_devices[handle];
if (!tfa98xx || !tfa98xx->regmap) {
pr_err("No tfa98xx regmap available\n");
return Tfa98xx_Error_Bad_Parameter;
}
retry:
ret = regmap_read(tfa98xx->regmap, subaddress, &value);
if (ret < 0) {
pr_warn("i2c error at subaddress 0x%x, retries left: %d\n", subaddress, retries);
if (retries) {
retries--;
msleep(I2C_RETRY_DELAY);
goto retry;
}
return Tfa98xx_Error_Fail;
}
*val = value & 0xffff;
if (tfa98xx_kmsg_regs)
dev_dbg(&tfa98xx->i2c->dev, "RD reg=0x%02x, val=0x%04x %s\n",
subaddress, *val,
ret<0? "Error!!" : "");
if (tfa98xx_ftrace_regs)
tfa98xx_trace_printk("\tRD reg=0x%02x, val=0x%04x %s\n",
subaddress, *val,
ret<0? "Error!!" : "");
} else {
pr_err("No device available\n");
error = Tfa98xx_Error_Fail;
}
return error;
}
enum Tfa98xx_Error tfa98xx_read_data(Tfa98xx_handle_t handle,
unsigned char reg,
int len, unsigned char value[])
{
enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
struct tfa98xx *tfa98xx;
struct i2c_client *tfa98xx_client;
int err;
int tries = 0;
struct i2c_msg msgs[] = {
{
.flags = 0,
.len = 1,
.buf = &reg,
}, {
.flags = I2C_M_RD,
.len = len,
.buf = value,
},
};
if (tfa98xx_devices[handle] && tfa98xx_devices[handle]->i2c) {
tfa98xx = tfa98xx_devices[handle];
tfa98xx_client = tfa98xx->i2c;
msgs[0].addr = tfa98xx_client->addr;
msgs[1].addr = tfa98xx_client->addr;
do {
err = i2c_transfer(tfa98xx_client->adapter, msgs,
ARRAY_SIZE(msgs));
if (err != ARRAY_SIZE(msgs))
msleep_interruptible(I2C_RETRY_DELAY);
} while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES));
if (err != ARRAY_SIZE(msgs)) {
dev_err(&tfa98xx_client->dev, "read transfer error %d\n",
err);
error = Tfa98xx_Error_Fail;
}
if (tfa98xx_kmsg_regs)
dev_dbg(&tfa98xx_client->dev, "RD-DAT reg=0x%02x, len=%d\n",
reg, len);
if (tfa98xx_ftrace_regs)
tfa98xx_trace_printk("\t\tRD-DAT reg=0x%02x, len=%d\n",
reg, len);
} else {
pr_err("No device available\n");
error = Tfa98xx_Error_Fail;
}
return error;
}
enum Tfa98xx_Error tfa98xx_write_raw(Tfa98xx_handle_t handle,
int len,
const unsigned char data[])
{
enum Tfa98xx_Error error = Tfa98xx_Error_Ok;
struct tfa98xx *tfa98xx;
int ret;
int retries = I2C_RETRIES;
if (tfa98xx_devices[handle]) {
tfa98xx = tfa98xx_devices[handle];
retry:
ret = i2c_master_send(tfa98xx->i2c, data, len);
if (ret < 0) {
pr_warn("i2c error, retries left: %d\n", retries);
if (retries) {
retries--;
msleep(I2C_RETRY_DELAY);
goto retry;
}
}
if (ret == len) {
if (tfa98xx_kmsg_regs)
dev_dbg(&tfa98xx->i2c->dev, " WR-RAW len=%d\n", len);
if (tfa98xx_ftrace_regs)
tfa98xx_trace_printk("\t\tWR-RAW len=%d\n", len);
return Tfa98xx_Error_Ok;
}
pr_err(" WR-RAW (len=%d) Error I2C send size mismatch %d\n", len, ret);
error = Tfa98xx_Error_Fail;
} else {
pr_err("No device available\n");
error = Tfa98xx_Error_Fail;
}
return error;
}
/* Read and return status_reg content, and intercept (interrupt related)
* events if any.
* mask can be used to ask to ignore some status bits.
*/
static unsigned int tfa98xx_read_status_reg(struct tfa98xx *tfa98xx,
unsigned int mask)
{
unsigned int reg;
/* interrupt bits to check */
unsigned int errs = TFA98XX_STATUSREG_WDS |
TFA98XX_STATUSREG_SPKS;
regmap_read(tfa98xx->regmap, TFA98XX_STATUSREG, &reg);
if (reg & errs & ~mask) {
/* interesting status bits to handle. Just trace for now. */
dev_info(tfa98xx->codec->dev, "status_reg events: 0x%x\n", reg);
}
return reg;
}
/* Interrupts management */
static void tfa98xx_interrupt_restore_tfa2(struct tfa98xx *tfa98xx)
{
unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8;
/* Write interrupt enable registers */
regmap_write(tfa98xx->regmap, base_addr_inten + 0,
handles_local[tfa98xx->handle].interrupt_enable[0]);
regmap_write(tfa98xx->regmap, base_addr_inten + 1,
handles_local[tfa98xx->handle].interrupt_enable[1]);
regmap_write(tfa98xx->regmap, base_addr_inten + 2,
handles_local[tfa98xx->handle].interrupt_enable[2]);
}
static void tfa98xx_interrupt_enable_tfa2(struct tfa98xx *tfa98xx, bool enable)
{
unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8;
if (enable) {
tfa98xx_interrupt_restore_tfa2(tfa98xx);
} else {
regmap_write(tfa98xx->regmap, base_addr_inten + 0, 0);
regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0);
regmap_write(tfa98xx->regmap, base_addr_inten + 2, 0);
}
}
/* Check if tap-detection can and shall be enabled.
* Configure SPK interrupt accordingly or setup polling mode
* Tap-detection shall be active if:
* - the service is enabled (tapdet_open), AND
* - the current profile is a tap-detection profile
* On TFA1 familiy of devices, activating tap-detection means enabling the SPK
* interrupt if available.
* We also update the tapdet_enabled and tapdet_poll variables.
*/
static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx)
{
unsigned int spkerr, enable = false;
unsigned int err;
int val, count = 0;
/* Support tap-detection on TFA1 family of devices */
if (!(tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) ||
(tfa98xx_dev_family(tfa98xx->handle)) != 1)
return;
if (tfa98xx->tapdet_open &&
(tfa98xx->tapdet_profiles & (1 << tfa98xx_profile)))
enable = true;
spkerr = enable ? 0 : 1;
if (!gpio_is_valid(tfa98xx->irq_gpio)) {
/* interrupt not available, setup polling mode */
tfa98xx->tapdet_poll = true;
if (enable)
queue_delayed_work(tfa98xx->tfa98xx_wq,
&tfa98xx->tapdet_work, HZ/10);
else
cancel_delayed_work_sync(&tfa98xx->tapdet_work);
dev_dbg(tfa98xx->codec->dev,
"Polling for tap-detection: %s (%d; 0x%x, %d)\n",
enable? "enabled":"disabled",
tfa98xx->tapdet_open, tfa98xx->tapdet_profiles,
tfa98xx_profile);
} else {
dev_dbg(tfa98xx->codec->dev,
"SPK interrupt for tap-detection: %s (%d; 0x%x, %d)\n",
enable? "enabled":"disabled",
tfa98xx->tapdet_open, tfa98xx->tapdet_profiles,
tfa98xx_profile);
/* update status_reg mask to match enabled interrupts */
handles_local[tfa98xx->handle].interrupt_status[0] &=
~TFA98XX_STATUSREG_SPKS;
handles_local[tfa98xx->handle].interrupt_status[0] |=
enable << TFA98XX_STATUSREG_SPKS_POS;
/* update interrupt_reg to match enabled interrupts */
handles_local[tfa98xx->handle].interrupt_enable[0] &=
~TFA98XX_INTERRUPT_REG_SPKD;
handles_local[tfa98xx->handle].interrupt_enable[0] |=
spkerr << TFA98XX_INTERRUPT_REG_SPKD_POS;
}
/* check disabled => enabled transition to clear pending events */
if (!tfa98xx->tapdet_enabled && enable) {
/* clear pending event if any */
err = tfa98xx_dsp_write_mem_word(tfa98xx->handle, XMEM_TAP_ACK, 0,
Tfa98xx_DMEM_XMEM);
if (err)
pr_info("Unable to write to XMEM\n");
val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS);
while ((TFA98XX_STATUSREG_SPKS & val) && (count < 50)) {
val = tfa98xx_read_status_reg(tfa98xx,
TFA98XX_STATUSREG_SPKS);
count++;
}
if (count > 1)
pr_info("Had to run %d times to ack SPKS at init\n", count);
}
tfa98xx->tapdet_enabled = enable;
if (!tfa98xx->tapdet_poll)
tfa98xx_interrupt_restore(tfa98xx);
}
/* Initial configuration of interrupt masks of devices for TFA1 family
* Disable all interrupts by default.
*/
static void tfa98xx_interrupt_setup_tfa1(struct tfa98xx *tfa98xx)
{
uint16_t ie_reg = 0;
/* disable all interrupt sources */
ie_reg = TFA98XX_INTERRUPT_REG_VDDD |
TFA98XX_INTERRUPT_REG_OTDD |
TFA98XX_INTERRUPT_REG_OVDD |
TFA98XX_INTERRUPT_REG_UVDD |
TFA98XX_INTERRUPT_REG_OCDD |
TFA98XX_INTERRUPT_REG_CLKD |
TFA98XX_INTERRUPT_REG_DCCD |
TFA98XX_INTERRUPT_REG_SPKD |
TFA98XX_INTERRUPT_REG_WDD;
/* preserve reserved value */
ie_reg |= 1 << 9;
/* Store requested setup */
handles_local[tfa98xx->handle].interrupt_enable[0] = ie_reg;
handles_local[tfa98xx->handle].interrupt_status[0] = 0;
dev_dbg(&tfa98xx->i2c->dev, "Initial interrupts setup: ICR = 0x%04x\n", ie_reg);
}
/* Restore for 1st generation of devices */
static void tfa98xx_interrupt_restore_tfa1(struct tfa98xx *tfa98xx)
{
unsigned int ie_reg = 0;
regmap_read(tfa98xx->regmap, TFA98XX_INTERRUPT_REG, &ie_reg);
if (ie_reg != handles_local[tfa98xx->handle].interrupt_enable[0]) {
ie_reg = handles_local[tfa98xx->handle].interrupt_enable[0];
/* Write interrupt enable registers */
regmap_write(tfa98xx->regmap, TFA98XX_INTERRUPT_REG, ie_reg);
dev_dbg(&tfa98xx->i2c->dev, "Restored interrupts: ICR = 0x%04x\n",
ie_reg);
} else {
dev_dbg(&tfa98xx->i2c->dev, "No interrupt restore needed\n");
}
}
/* Enable for 1st generation of devices */
static void tfa98xx_interrupt_enable_tfa1(struct tfa98xx *tfa98xx, bool enable)
{
handles_local[tfa98xx->handle].interrupt_enable[0] &= ~TFA98XX_INTERRUPT_REG_INT;
handles_local[tfa98xx->handle].interrupt_enable[0] |= enable << TFA98XX_INTERRUPT_REG_INT_POS;
tfa98xx_interrupt_restore_tfa1(tfa98xx);
}
static void tfa98xx_interrupt_setup_tfa2(struct tfa98xx *tfa98xx)
{
uint16_t ie_reg;
handles_local[tfa98xx->handle].interrupt_enable[0] = 0;
ie_reg = 0;
TFA_SET_BF_VALUE(tfa98xx->handle, IEMWSRC, 1, &ie_reg);
handles_local[tfa98xx->handle].interrupt_enable[1] = ie_reg;
handles_local[tfa98xx->handle].interrupt_enable[2] = 0;
}
/* Initial SW configuration for interrupts. Does not enable HW interrupts. */
static void tfa98xx_interrupt_setup(struct tfa98xx *tfa98xx)
{
if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS)
return;
if (tfa98xx->flags & TFA98XX_FLAG_TFA9890_FAM_DEV)
tfa98xx_interrupt_setup_tfa1(tfa98xx);
else
tfa98xx_interrupt_setup_tfa2(tfa98xx);
}
/* Restore interrupt setup in case it would be lost (at device cold-start) */
static void tfa98xx_interrupt_restore(struct tfa98xx *tfa98xx)
{
if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS)
return;
if (tfa98xx_dev_family(tfa98xx->handle) == 2)
tfa98xx_interrupt_restore_tfa2(tfa98xx);
else
tfa98xx_interrupt_restore_tfa1(tfa98xx);
}
/* global enable / disable interrupts */
static void tfa98xx_interrupt_enable(struct tfa98xx *tfa98xx, bool enable)
{
if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS)
return;
if (tfa98xx_dev_family(tfa98xx->handle) == 2)
tfa98xx_interrupt_enable_tfa2(tfa98xx, enable);
else
tfa98xx_interrupt_enable_tfa1(tfa98xx, enable);
}
/* Firmware management
* Downloaded once only at module init
* FIXME: may need to review that (one per instance of codec device?)
*/
static char *fw_name = "tfa98xx.cnt";
module_param(fw_name, charp, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(fw_name, "TFA98xx DSP firmware (container file) name.");
static nxpTfaContainer_t *container;
static void tfa98xx_container_loaded(const struct firmware *cont, void *context)
{
struct tfa98xx *tfa98xx = context;
enum tfa_error tfa_err;
int container_size;
int handle;
int ret;
tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL;
if (!cont) {
pr_err("Failed to read %s\n", fw_name);
return;
}
pr_debug("loaded %s - size: %zu\n", fw_name,
cont ? cont->size : 0);
container = kzalloc(cont->size, GFP_KERNEL);
if (!container) {
release_firmware(cont);
pr_err("Error allocating memory\n");
return;
}
container_size = cont->size;
memcpy(container, cont->data, container_size);
release_firmware(cont);
pr_debug("%.2s%.2s\n", container->version, container->subversion);
pr_debug("%.8s\n", container->customer);
pr_debug("%.8s\n", container->application);
pr_debug("%.8s\n", container->type);
pr_debug("%d ndev\n", container->ndev);
pr_debug("%d nprof\n", container->nprof);
tfa_err = tfa_load_cnt(container, container_size);
if (tfa_err != tfa_error_ok) {
dev_err(tfa98xx->dev, "Cannot load container file, aborting\n");
return;
}
/* register codec with dsp */
tfa98xx->handle = tfa98xx_register_dsp(tfa98xx);
if (tfa98xx->handle < 0) {
dev_err(tfa98xx->dev, "Cannot register with DSP, aborting\n");
return;
}
if (tfa_probe(tfa98xx->i2c->addr << 1, &handle) != Tfa98xx_Error_Ok) {
dev_err(tfa98xx->dev, "Failed to probe TFA98xx @ 0x%.2x\n", tfa98xx->i2c->addr);
return;
}
/* prefix is the application name from the cnt */
tfa_cnt_get_app_name(tfa98xx->fw.name);
/* Override default profile if requested */
if (strcmp(dflt_prof_name, "")) {
unsigned int i;
for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) {
if (strcmp(tfaContProfileName(tfa98xx->handle, i),
dflt_prof_name) == 0) {
tfa98xx_profile = i;
dev_info(tfa98xx->dev,
"changing default profile to %s (%d)\n",
dflt_prof_name, tfa98xx_profile);
break;
}
}
if (i >= tfaContMaxProfile(tfa98xx->handle))
dev_info(tfa98xx->dev,
"Default profile override failed (%s profile not found)\n",
dflt_prof_name);
}
tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_OK;
pr_debug("Firmware init complete\n");
if (no_start != 0)
return;
/* Only controls for master device */
if (tfa98xx->handle == 0)
tfa98xx_create_controls(tfa98xx);
tfa98xx_inputdev_check_register(tfa98xx);
if (tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE) {
tfa98xx_interrupt_enable(tfa98xx, true);
return;
}
mutex_lock(&tfa98xx->dsp_lock);
ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps);
if (ret == Tfa98xx_Error_Ok)
tfa98xx->dsp_init = TFA98XX_DSP_INIT_DONE;
mutex_unlock(&tfa98xx->dsp_lock);
tfa98xx_interrupt_enable(tfa98xx, true);
}
static int tfa98xx_load_container(struct tfa98xx *tfa98xx)
{
tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_PENDING;
return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
fw_name, tfa98xx->dev, GFP_KERNEL,
tfa98xx, tfa98xx_container_loaded);
}
static void tfa98xx_tapdet(struct tfa98xx *tfa98xx)
{
unsigned int mem;
int err, btn, count = 0;
uint16_t val;
/* check tap pattern (BTN_0 is "error" wrong tap indication */
tfa98xx_dsp_read_mem(tfa98xx->handle, XMEM_TAP_READ, 1, &mem);
switch (mem) {
case 0xffffffff:
pr_info("More than 4 taps detected! (flagTapPattern = -1)\n");
btn = BTN_0;
break;
case 0xfffffffe:
pr_info("Single tap detected! (flagTapPattern = -2)\n");
btn = BTN_0;
break;
case 0:
pr_info("Unrecognized pattern! (flagTapPattern = 0)\n");
btn = BTN_0;
break;
default:
pr_info("Detected pattern: %d\n", mem);
btn = BTN_0 + mem;
break;
}
input_report_key(tfa98xx->input, btn, 1);
input_report_key(tfa98xx->input, btn, 0);
input_sync(tfa98xx->input);
/* acknowledge event */
err = tfa98xx_dsp_write_mem_word(tfa98xx->handle, XMEM_TAP_ACK, 0, Tfa98xx_DMEM_XMEM);
if (err)
pr_info("Unable to write to XMEM\n");
val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS);
while ((TFA98XX_STATUSREG_SPKS & val) && (count < 50)) {
val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS);
count++;
}
if (count > 1)
pr_info("Had to run %d times to ack SPKS\n", count);
}
static void tfa98xx_tapdet_work(struct work_struct *work)
{
struct tfa98xx *tfa98xx;
u16 val;
tfa98xx = container_of(work, struct tfa98xx, tapdet_work.work);
/* Check for SPKS bit*/
val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG);
if (val & TFA98XX_STATUSREG_SPKS)
tfa98xx_tapdet(tfa98xx);
queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->tapdet_work, HZ/10);
}
static void tfa98xx_monitor(struct work_struct *work)
{
struct tfa98xx *tfa98xx;
u16 val;
u16 cfe = 0;
tfa98xx = container_of(work, struct tfa98xx, monitor_work.work);
/* Check for tap-detection - bypass monitor if it is active */
if (!tfa98xx->input) {
/*
* check IC status bits: cold start
* and DSP watch dog bit to re init
*/
val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG);
pr_debug("SYS_STATUS0: 0x%04x\n", val);
cfe = snd_soc_read(tfa98xx->codec, TFA98XX_SYS_CTRL) & TFA98XX_SYS_CTRL_CFE;
if ((cfe && (TFA98XX_STATUSREG_ACS & val)) ||
(TFA98XX_STATUSREG_WDS & val)) {
tfa98xx->dsp_init = TFA98XX_DSP_INIT_RECOVER;
if (TFA98XX_STATUSREG_ACS & val)
pr_err("ERROR: ACS\n");
if (TFA98XX_STATUSREG_WDS & val)
pr_err("ERROR: WDS\n");
queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->init_work, 0);
}
if (TFA98XX_STATUSREG_SPKS & val)
pr_err("ERROR: SPKS\n");
if (!(TFA98XX_STATUSREG_SWS & val))
pr_err("ERROR: SWS\n");
/* Check secondary errors */
if ( !(val & TFA98XX_STATUSREG_CLKS) ||
!(val & TFA98XX_STATUSREG_UVDS) ||
!(val & TFA98XX_STATUSREG_OVDS) ||
!(val & TFA98XX_STATUSREG_OTDS) ||
!(val & TFA98XX_STATUSREG_PLLS) ||
!(val & TFA98XX_STATUSREG_VDDS))
pr_err("Misc errors detected: STATUS_FLAG0 = 0x%x\n", val);
if (tfa98xx_dev_family(tfa98xx->handle) == 2) {
val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUS_FLAGS1);
if ((val & TFA98XX_STATUS_FLAGS1_TDMERR) |
(val & (0x6 << TFA98XX_STATUS_FLAGS1_TDMSTAT_POS)) |
(val & TFA98XX_STATUS_FLAGS1_TDMLUTER))
pr_err("TDM related errors: STATUS_FLAG1 = 0x%x\n", val);
}
}
/* reschedule */
queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->monitor_work, 5*HZ);
}
static void tfa98xx_dsp_init(struct tfa98xx *tfa98xx)
{
int ret;
bool failed = false;
bool reschedule = false;
if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) {
pr_debug("Skipping tfa_start (no FW: %d)\n", tfa98xx->dsp_fw_state);
return;
}
if(tfa98xx->dsp_init == TFA98XX_DSP_INIT_DONE) {
pr_debug("Stream already started, skipping DSP power-on\n");
return;
}
mutex_lock(&tfa98xx->dsp_lock);
tfa98xx->dsp_init = TFA98XX_DSP_INIT_PENDING;
if (tfa98xx->init_count < TF98XX_MAX_DSP_START_TRY_COUNT) {
/* directly try to start DSP */
ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps);
if (ret != Tfa98xx_Error_Ok) {
/* It may fail as we may not have a valid clock at that
* time, so re-schedule and re-try later.
*/
dev_err(&tfa98xx->i2c->dev,
"tfa_start failed! (err %d) - %d\n",
ret, tfa98xx->init_count);
reschedule = true;
} else {
/* Subsystem ready, tfa init complete */
dev_dbg(&tfa98xx->i2c->dev,
"tfa_start success (%d)\n",
tfa98xx->init_count);
/* cancel other pending init works */
cancel_delayed_work(&tfa98xx->init_work);
tfa98xx->init_count = 0;
/*
* start monitor thread to check IC status bit
* periodically, and re-init IC to recover if
* needed.
*/
queue_delayed_work(tfa98xx->tfa98xx_wq,
&tfa98xx->monitor_work,
1*HZ);
}
} else {
/* exceeded max number ot start tentatives, cancel start */
dev_err(&tfa98xx->i2c->dev,
"Failed starting device (%d)\n",
tfa98xx->init_count);
failed = true;
}
if (reschedule) {
/* reschedule this init work for later */
queue_delayed_work(tfa98xx->tfa98xx_wq,
&tfa98xx->init_work,
msecs_to_jiffies(5));
tfa98xx->init_count++;
}
if (failed) {
tfa98xx->dsp_init = TFA98XX_DSP_INIT_FAIL;
/* cancel other pending init works */
cancel_delayed_work(&tfa98xx->init_work);
tfa98xx->init_count = 0;
}
mutex_unlock(&tfa98xx->dsp_lock);
return;
}
static void tfa98xx_dsp_init_work(struct work_struct *work)
{
struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, init_work.work);
/* Only do dsp init for master device */
if (tfa98xx->handle != 0)
return;
tfa98xx_dsp_init(tfa98xx);
}
static void tfa98xx_interrupt(struct work_struct *work)
{
struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, interrupt_work.work);
unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8;
unsigned int base_addr_ist = TFA_FAM(tfa98xx->handle,ISTVDDS) >> 8;
unsigned int base_addr_icl = TFA_FAM(tfa98xx->handle,ICLVDDS) >> 8;
//unsigned int base_addr_ipo = TFA_FAM(tfa98xx->handle,IPOVDDS) >> 8;
u32 out1, out2, out3;
pr_info("\n");
regmap_read(tfa98xx->regmap, base_addr_ist + 0, &out1);
regmap_read(tfa98xx->regmap, base_addr_ist + 1, &out2);
regmap_read(tfa98xx->regmap, base_addr_ist + 2, &out3);
out1 &= handles_local[tfa98xx->handle].interrupt_enable[0];
out2 &= handles_local[tfa98xx->handle].interrupt_enable[1];
out3 &= handles_local[tfa98xx->handle].interrupt_enable[2];
if (out1) {
/* clear and enable interrupt(s) again */
regmap_write(tfa98xx->regmap, base_addr_icl + 0, out1);
regmap_write(tfa98xx->regmap, base_addr_inten + 0,
handles_local[tfa98xx->handle].interrupt_enable[0]);
}
if (out2) {
/* manager wait for source state */
if (TFA_GET_BF_VALUE(tfa98xx->handle, ISTMWSRC, out2) > 0) {
int manwait1 = TFA_GET_BF(tfa98xx->handle, MANWAIT1);
if (manwait1 > 0) {
pr_info("entering wait for source state\n");
tfa98xx->count_wait_for_source_state++;
/* set AMPC and AMPE to make sure the amp is enabled */
pr_info("setting AMPC and AMPE to 1 (default) \n");
TFA_SET_BF(tfa98xx->handle, AMPC, 1);
TFA_SET_BF(tfa98xx->handle, AMPE, 1);
/* set MANSCONF here, the manager will continue if clock is there */
TFA_SET_BF(tfa98xx->handle, MANSCONF, 1);
} else {
/* Now we can switch profile with internal clock it is not required to call tfa_start */
pr_info("leaving wait for source state\n");
TFA_SET_BF(tfa98xx->handle, MANSCONF, 0);
}
if (manwait1 > 0)
TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 0);
else
TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1);
}
/* clear and enable interrupt(s) again */
regmap_write(tfa98xx->regmap, base_addr_icl + 1, out2);
regmap_write(tfa98xx->regmap, base_addr_inten + 1,
handles_local[tfa98xx->handle].interrupt_enable[1]);
}
if (out3) {
/* clear and enable interrupt(s) again */
regmap_write(tfa98xx->regmap, base_addr_icl + 2, out3);
regmap_write(tfa98xx->regmap, base_addr_inten + 2,
handles_local[tfa98xx->handle].interrupt_enable[2]);
}
}
static int tfa98xx_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
unsigned int sr;
int len, prof, nprof = tfaContMaxProfile(tfa98xx->handle), idx = 0;
char *basename;
/*
* Support CODEC to CODEC links,
* these are called with a NULL runtime pointer.
*/
if (!substream->runtime)
return 0;
if (no_start != 0)
return 0;
basename = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL);
if (!basename)
return -ENOMEM;
/* copy profile name into basename until the . */
get_profile_basename(basename, tfaContProfileName(tfa98xx->handle, tfa98xx_profile));
len = strlen(basename);
/* loop over all profiles and get the supported samples rate(s) from
* the profiles with the same basename
*/
for (prof = 0; prof < nprof; prof++) {
if (0 == strncmp(basename, tfaContProfileName(tfa98xx->handle, prof), len)) {
/* Check which sample rate is supported with current profile,
* and enforce this.
*/
sr = tfa98xx_get_profile_sr(tfa98xx->handle, prof);
if (!sr)
dev_info(codec->dev, "Unable to identify supported sample rate\n");
tfa98xx->rate_constraint_list[idx++] = sr;
tfa98xx->rate_constraint.count += 1;
}
}
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&tfa98xx->rate_constraint);
}
static int tfa98xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec_dai->codec);
tfa98xx->sysclk = freq;
return 0;
}
static int tfa98xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(dai->codec);
struct snd_soc_codec *codec = dai->codec;
pr_debug("fmt=0x%x\n", fmt);
/* Supported mode: regular I2S, slave, or PDM */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
dev_err(codec->dev, "Invalid Codec master mode\n");
return -EINVAL;
}
break;
case SND_SOC_DAIFMT_PDM:
break;
default:
dev_err(codec->dev, "Unsupported DAI format %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
tfa98xx->audio_mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
return 0;
}
static int tfa98xx_get_fssel(unsigned int rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(rate_to_fssel); i++) {
if (rate_to_fssel[i].rate == rate) {
return rate_to_fssel[i].fssel;
}
}
return -EINVAL;
}
static int tfa98xx_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
unsigned int rate;
int prof_idx;
/* Supported */
rate = params_rate(params);
pr_debug("Requested rate: %d, sample size: %d, physical size: %d\n",
rate, snd_pcm_format_width(params_format(params)),
snd_pcm_format_physical_width(params_format(params)));
if (params_channels(params) > 2) {
pr_warn("Unusual number of channels: %d\n", params_channels(params));
}
if (no_start != 0)
return 0;
/* check if samplerate is supported for this mixer profile */
prof_idx = get_profile_id_for_sr(tfa98xx_mixer_profile, rate);
if (prof_idx < 0) {
pr_err("tfa98xx: invalid sample rate %d.\n", rate);
return -EINVAL;
}
pr_debug("mixer profile:container profile = [%d:%d]\n", tfa98xx_mixer_profile, prof_idx);
/* update 'real' profile (container profile) */
tfa98xx_profile = prof_idx;
/* update to new rate */
tfa98xx->rate = rate;
return 0;
}
static int tfa98xx_mute(struct snd_soc_dai *dai, int mute, int stream)
{
struct snd_soc_codec *codec = dai->codec;
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
dev_dbg(&tfa98xx->i2c->dev, "state: %d\n", mute);
if (!(tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE))
return 0;
if (no_start) {
pr_debug("no_start parameter set no tfa_start or tfa_stop, returning\n");
return 0;
}
if (mute) {
/* stop DSP only when both playback and capture streams
* are deactivated
*/
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
tfa98xx->pstream = 0;
else
tfa98xx->cstream = 0;
if (tfa98xx->pstream != 0 || tfa98xx->cstream != 0)
return 0;
cancel_delayed_work_sync(&tfa98xx->monitor_work);
cancel_delayed_work_sync(&tfa98xx->init_work);
if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK)
return 0;
mutex_lock(&tfa98xx->dsp_lock);
tfa_stop();
tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED;
mutex_unlock(&tfa98xx->dsp_lock);
} else {
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
tfa98xx->pstream = 1;
else
tfa98xx->cstream = 1;
/* Start DSP */
if (tfa98xx->dsp_init != TFA98XX_DSP_INIT_PENDING)
queue_delayed_work(tfa98xx->tfa98xx_wq,
&tfa98xx->init_work,
0);
}
return 0;
}
static const struct snd_soc_dai_ops tfa98xx_dai_ops = {
.startup = tfa98xx_startup,
.set_fmt = tfa98xx_set_fmt,
.set_sysclk = tfa98xx_set_dai_sysclk,
.hw_params = tfa98xx_hw_params,
.mute_stream = tfa98xx_mute,
};
static struct snd_soc_dai_driver tfa98xx_dai[] = {
{
.name = "tfa98xx-aif",
.base = TFA98XX_TDM_CONFIG0 - 1,
.id = 1,
.playback = {
.stream_name = "AIF Playback",
.channels_min = 1,
.channels_max = 2,
.rates = TFA98XX_RATES,
.formats = TFA98XX_FORMATS,
},
.capture = {
.stream_name = "AIF Capture",
.channels_min = 1,
.channels_max = 2,
.rates = TFA98XX_RATES,
.formats = TFA98XX_FORMATS,
},
.ops = &tfa98xx_dai_ops,
.symmetric_rates = 1,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
.symmetric_channels = 1,
.symmetric_samplebits = 1,
#endif
},
};
static int tfa98xx_probe(struct snd_soc_codec *codec)
{
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
int ret;
pr_debug("\n");
tfa98xx->rate_constraint.list = &tfa98xx->rate_constraint_list[0];
tfa98xx->rate_constraint.count =
ARRAY_SIZE(tfa98xx->rate_constraint_list);
/* setup work queue, will be used to initial DSP on first boot up */
tfa98xx->tfa98xx_wq = create_singlethread_workqueue("tfa98xx");
if (!tfa98xx->tfa98xx_wq)
return -ENOMEM;
INIT_DELAYED_WORK(&tfa98xx->init_work, tfa98xx_dsp_init_work);
INIT_DELAYED_WORK(&tfa98xx->monitor_work, tfa98xx_monitor);
INIT_DELAYED_WORK(&tfa98xx->interrupt_work, tfa98xx_interrupt);
INIT_DELAYED_WORK(&tfa98xx->tapdet_work, tfa98xx_tapdet_work);
tfa98xx->codec = codec;
ret = tfa98xx_load_container(tfa98xx);
pr_debug("Container loading requested: %d\n", ret);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
codec->control_data = tfa98xx->regmap;
ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
}
#endif
tfa98xx_add_widgets(tfa98xx);
dev_info(codec->dev, "tfa98xx codec registered (%s)",
tfa98xx->fw.name);
return ret;
}
static int tfa98xx_remove(struct snd_soc_codec *codec)
{
struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec);
pr_debug("\n");
tfa98xx_inputdev_unregister(tfa98xx);
cancel_delayed_work_sync(&tfa98xx->interrupt_work);
cancel_delayed_work_sync(&tfa98xx->monitor_work);
cancel_delayed_work_sync(&tfa98xx->init_work);
cancel_delayed_work_sync(&tfa98xx->tapdet_work);
if (tfa98xx->tfa98xx_wq)
destroy_workqueue(tfa98xx->tfa98xx_wq);
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
struct regmap *tfa98xx_get_regmap(struct device *dev)
{
struct tfa98xx *tfa98xx = dev_get_drvdata(dev);
return tfa98xx->regmap;
}
#endif
static struct snd_soc_codec_driver soc_codec_dev_tfa98xx = {
.probe = tfa98xx_probe,
.remove = tfa98xx_remove,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
.get_regmap = tfa98xx_get_regmap,
#endif
};
static bool tfa98xx_writeable_register(struct device *dev, unsigned int reg)
{
/* enable read access for all registers */
return 1;
}
static bool tfa98xx_readable_register(struct device *dev, unsigned int reg)
{
/* enable read access for all registers */
return 1;
}
static bool tfa98xx_volatile_register(struct device *dev, unsigned int reg)
{
/* enable read access for all registers */
return 1;
}
static const struct regmap_config tfa98xx_regmap = {
.reg_bits = 8,
.val_bits = 16,
.max_register = TFA98XX_MAX_REGISTER,
.writeable_reg = tfa98xx_writeable_register,
.readable_reg = tfa98xx_readable_register,
.volatile_reg = tfa98xx_volatile_register,
.cache_type = REGCACHE_NONE,
};
static void tfa98xx_irq_tfa2(struct tfa98xx *tfa98xx)
{
unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8;
unsigned int base_addr_ist = TFA_FAM(tfa98xx->handle,ISTVDDS) >> 8;
u32 en1, en2, en3;
u32 out1 = 0, out2 = 0, out3 = 0;
pr_info("\n");
regmap_read(tfa98xx->regmap, base_addr_inten + 0, &en1);
regmap_read(tfa98xx->regmap, base_addr_inten + 1, &en2);
regmap_read(tfa98xx->regmap, base_addr_inten + 2, &en3);
regmap_read(tfa98xx->regmap, base_addr_ist + 0, &out1);
regmap_read(tfa98xx->regmap, base_addr_ist + 1, &out2);
regmap_read(tfa98xx->regmap, base_addr_ist + 2, &out3);
pr_info("interrupt1: 0x%.4x (enabled: 0x%.4x)\n", out1, en1);
pr_info("interrupt2: 0x%.4x (enabled: 0x%.4x)\n", out2, en2);
pr_info("interrupt3: 0x%.4x (enabled: 0x%.4x)\n", out3, en3);
out1 &= en1;
out2 &= en2;
out3 &= en3;
en1 = handles_local[tfa98xx->handle].interrupt_enable[0] ^ out1;
en2 = handles_local[tfa98xx->handle].interrupt_enable[1] ^ out2;
en3 = handles_local[tfa98xx->handle].interrupt_enable[2] ^ out3;
regmap_write(tfa98xx->regmap, base_addr_inten + 0, en1);
regmap_write(tfa98xx->regmap, base_addr_inten + 1, en2);
regmap_write(tfa98xx->regmap, base_addr_inten + 2, en3);
if (out1 || out2 || out3)
queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->interrupt_work, 0);
}
static void __tfa98xx_irq(struct tfa98xx *tfa98xx)
{
uint16_t val;
uint16_t ie = handles_local[tfa98xx->handle].interrupt_status[0];
val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG);
dev_info(&tfa98xx->i2c->dev, "interrupt: 0x%04x (enabled: 0x%04x)\n", val, ie);
#ifdef DEBUG
if (!(val & ie)) {
unsigned int ireg;
/* interrupt triggered while all interrupt sources supposedly
* disabled
*/
ireg = snd_soc_read(tfa98xx->codec, TFA98XX_INTERRUPT_REG);
dev_dbg(&tfa98xx->i2c->dev, "ICR: 0x%04x\n", ireg);
}
#endif
val &= ie;
/* Check for SPKS bit */
if (val & TFA98XX_STATUSREG_SPKS)
tfa98xx_tapdet(tfa98xx);
}
static irqreturn_t tfa98xx_irq(int irq, void *data)
{
struct tfa98xx *tfa98xx = data;
if (tfa98xx_dev_family(tfa98xx->handle) == 2)
tfa98xx_irq_tfa2(tfa98xx);
else
__tfa98xx_irq(tfa98xx);
return IRQ_HANDLED;
}
static int tfa98xx_ext_reset(struct tfa98xx *tfa98xx)
{
if (tfa98xx && gpio_is_valid(tfa98xx->reset_gpio)) {
gpio_set_value_cansleep(tfa98xx->reset_gpio, 1);
gpio_set_value_cansleep(tfa98xx->reset_gpio, 0);
}
return 0;
}
static int tfa98xx_parse_dt(struct device *dev, struct tfa98xx *tfa98xx,
struct device_node *np) {
tfa98xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
if (tfa98xx->reset_gpio < 0)
dev_dbg(dev, "No reset GPIO provided, will not HW reset device\n");
tfa98xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0);
if (tfa98xx->irq_gpio < 0)
dev_dbg(dev, "No IRQ GPIO provided.\n");
return 0;
}
static ssize_t tfa98xx_reg_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct tfa98xx *tfa98xx = dev_get_drvdata(dev);
if (count != 1) {
pr_debug("invalid register address");
return -EINVAL;
}
tfa98xx->reg = buf[0];
return 1;
}
static ssize_t tfa98xx_rw_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct tfa98xx *tfa98xx = dev_get_drvdata(dev);
u8 *data;
int ret;
int retries = I2C_RETRIES;
data = kmalloc(count+1, GFP_KERNEL);
if (data == NULL) {
pr_debug("can not allocate memory\n");
return -ENOMEM;
}
data[0] = tfa98xx->reg;
memcpy(&data[1], buf, count);
retry:
ret = i2c_master_send(tfa98xx->i2c, data, count+1);
if (ret < 0) {
pr_warn("i2c error, retries left: %d\n", retries);
if (retries) {
retries--;
msleep(I2C_RETRY_DELAY);
goto retry;
}
}
kfree(data);
return ret;
}
static ssize_t tfa98xx_rw_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct tfa98xx *tfa98xx = dev_get_drvdata(dev);
struct i2c_msg msgs[] = {
{
.addr = tfa98xx->i2c->addr,
.flags = 0,
.len = 1,
.buf = &tfa98xx->reg,
},
{
.addr = tfa98xx->i2c->addr,
.flags = I2C_M_RD,
.len = count,
.buf = buf,
},
};
int ret;
int retries = I2C_RETRIES;
retry:
ret = i2c_transfer(tfa98xx->i2c->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0) {
pr_warn("i2c error, retries left: %d\n", retries);
if (retries) {
retries--;
msleep(I2C_RETRY_DELAY);
goto retry;
}
return ret;
}
/* ret contains the number of i2c messages send */
return 1 + ((ret > 1) ? count : 0);
}
static struct bin_attribute dev_attr_rw = {
.attr = {
.name = "rw",
.mode = S_IRUSR | S_IWUSR,
},
.size = 0,
.read = tfa98xx_rw_read,
.write = tfa98xx_rw_write,
};
static struct bin_attribute dev_attr_reg = {
.attr = {
.name = "reg",
.mode = S_IWUSR,
},
.size = 0,
.read = NULL,
.write = tfa98xx_reg_write,
};
static int tfa98xx_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_dai_driver *dai;
struct tfa98xx *tfa98xx;
struct device_node *np = i2c->dev.of_node;
int irq_flags;
unsigned int reg;
int ret;
pr_info("%s\n", __func__);
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
dev_err(&i2c->dev, "check_functionality failed\n");
return -EIO;
}
tfa98xx = devm_kzalloc(&i2c->dev, sizeof(struct tfa98xx),
GFP_KERNEL);
if (tfa98xx == NULL)
return -ENOMEM;
tfa98xx->dev = &i2c->dev;
tfa98xx->i2c = i2c;
tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED;
tfa98xx->rate = 48000; /* init to the default sample rate (48kHz) */
tfa98xx->regmap = devm_regmap_init_i2c(i2c, &tfa98xx_regmap);
if (IS_ERR(tfa98xx->regmap)) {
ret = PTR_ERR(tfa98xx->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
goto err;
}
i2c_set_clientdata(i2c, tfa98xx);
mutex_init(&tfa98xx->dsp_lock);
init_waitqueue_head(&tfa98xx->wq);
if (np) {
ret = tfa98xx_parse_dt(&i2c->dev, tfa98xx, np);
if (ret) {
dev_err(&i2c->dev, "Failed to parse DT node\n");
goto err;
}
if (no_start)
tfa98xx->irq_gpio = -1;
} else {
tfa98xx->reset_gpio = -1;
tfa98xx->irq_gpio = -1;
}
if (gpio_is_valid(tfa98xx->reset_gpio)) {
ret = devm_gpio_request_one(&i2c->dev, tfa98xx->reset_gpio,
GPIOF_OUT_INIT_LOW, "TFA98XX_RST");
if (ret)
goto err;
}
if (gpio_is_valid(tfa98xx->irq_gpio)) {
ret = devm_gpio_request_one(&i2c->dev, tfa98xx->irq_gpio,
GPIOF_DIR_IN, "TFA98XX_INT");
if (ret)
goto err;
}
/* Power up! */
tfa98xx_ext_reset(tfa98xx);
if (no_start == 0) {
ret = regmap_read(tfa98xx->regmap, 0x03, &reg);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to read Revision register: %d\n",
ret);
return -EIO;
}
switch (reg & 0xff) {
case 0x88: /* tfa9888 */
pr_info("TFA9888 detected\n");
tfa98xx->flags |= TFA98XX_FLAG_STEREO_DEVICE;
tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS;
break;
case 0x80: /* tfa9890 */
case 0x81: /* tfa9890 */
pr_info("TFA9890 detected\n");
tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE;
tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS;
tfa98xx->flags |= TFA98XX_FLAG_TFA9890_FAM_DEV;
break;
case 0x92: /* tfa9891 */
pr_info("TFA9891 detected\n");
tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE;
tfa98xx->flags |= TFA98XX_FLAG_SAAM_AVAILABLE;
tfa98xx->flags |= TFA98XX_FLAG_TAPDET_AVAILABLE;
tfa98xx->flags |= TFA98XX_FLAG_TFA9890_FAM_DEV;
break;
case 0x97:
pr_info("TFA9897 detected\n");
tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE;
tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS;
tfa98xx->flags |= TFA98XX_FLAG_TFA9897_FAM_DEV;
break;
default:
pr_info("Unsupported device revision (0x%x)\n", reg & 0xff);
return -EINVAL;
}
}
/* Modify the stream names, by appending the i2c device address.
* This is used with multicodec, in order to discriminate the devices.
* Stream names appear in the dai definition and in the stream .
* We create copies of original structures because each device will
* have its own instance of this structure, with its own address.
*/
dai = devm_kzalloc(&i2c->dev, sizeof(tfa98xx_dai), GFP_KERNEL);
if (!dai)
return -ENOMEM;
memcpy(dai, tfa98xx_dai, sizeof(tfa98xx_dai));
#if 0
tfa98xx_append_i2c_address(&i2c->dev,
i2c,
NULL,
0,
dai,
ARRAY_SIZE(tfa98xx_dai));
#endif
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_tfa98xx, dai,
ARRAY_SIZE(tfa98xx_dai));
if (ret < 0) {
dev_err(&i2c->dev, "Failed to register TFA98xx: %d\n", ret);
goto err_off;
}
if (gpio_is_valid(tfa98xx->irq_gpio) &&
!(tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS)) {
/* register irq handler */
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
ret = devm_request_threaded_irq(&i2c->dev,
gpio_to_irq(tfa98xx->irq_gpio),
NULL, tfa98xx_irq, irq_flags,
"tfa98xx", tfa98xx);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
gpio_to_irq(tfa98xx->irq_gpio), ret);
goto err_off;
}
tfa98xx_interrupt_setup(tfa98xx);
} else {
dev_info(&i2c->dev, "Skipping IRQ registration\n");
/* disable feature support if gpio was invalid */
tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS;
}
#ifdef CONFIG_DEBUG_FS
tfa98xx_debug_init(tfa98xx, i2c);
#endif
/* Register the sysfs files for climax backdoor access */
ret = device_create_bin_file(&i2c->dev, &dev_attr_rw);
if (ret)
dev_info(&i2c->dev, "error creating sysfs files\n");
ret = device_create_bin_file(&i2c->dev, &dev_attr_reg);
if (ret)
dev_info(&i2c->dev, "error creating sysfs files\n");
pr_info("%s Probe completed successfully!\n", __func__);
return 0;
err_off:
tfa98xx_unregister_dsp(tfa98xx);
err:
return ret;
}
static int tfa98xx_i2c_remove(struct i2c_client *i2c)
{
struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c);
pr_debug("\n");
cancel_delayed_work_sync(&tfa98xx->interrupt_work);
cancel_delayed_work_sync(&tfa98xx->monitor_work);
cancel_delayed_work_sync(&tfa98xx->init_work);
cancel_delayed_work_sync(&tfa98xx->tapdet_work);
device_remove_bin_file(&i2c->dev, &dev_attr_reg);
device_remove_bin_file(&i2c->dev, &dev_attr_rw);
#ifdef CONFIG_DEBUG_FS
tfa98xx_debug_remove(tfa98xx);
#endif
tfa98xx_unregister_dsp(tfa98xx);
snd_soc_unregister_codec(&i2c->dev);
if (gpio_is_valid(tfa98xx->irq_gpio))
devm_gpio_free(&i2c->dev, tfa98xx->irq_gpio);
if (gpio_is_valid(tfa98xx->reset_gpio))
devm_gpio_free(&i2c->dev, tfa98xx->reset_gpio);
return 0;
}
static const struct i2c_device_id tfa98xx_i2c_id[] = {
{ "tfa98xx", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tfa98xx_i2c_id);
#ifdef CONFIG_OF
static struct of_device_id tfa98xx_dt_match[] = {
{ .compatible = "nxp,tfa98xx" },
{ .compatible = "nxp,tfa9890" },
{ .compatible = "nxp,tfa9891" },
{ .compatible = "nxp,tfa9888" },
{ },
};
#endif
static struct i2c_driver tfa98xx_i2c_driver = {
.driver = {
.name = "tfa98xx",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tfa98xx_dt_match),
},
.probe = tfa98xx_i2c_probe,
.remove = tfa98xx_i2c_remove,
.id_table = tfa98xx_i2c_id,
};
static int trace_level = 0;
module_param(trace_level, int, S_IRUGO);
MODULE_PARM_DESC(trace_level, "TFA98xx debug trace level (0=off, bits:1=verbose,2=regdmesg,3=regftrace).");
static int __init tfa98xx_i2c_init(void)
{
int ret = 0;
pr_info("TFA98XX driver version %s\n", TFA98XX_VERSION);
/* Enable debug traces */
tfa_verbose(trace_level);
tfa98xx_kmsg_regs = trace_level & 2;
tfa98xx_ftrace_regs = trace_level & 4;
ret = i2c_add_driver(&tfa98xx_i2c_driver);
return ret;
}
module_init(tfa98xx_i2c_init);
static void __exit tfa98xx_i2c_exit(void)
{
i2c_del_driver(&tfa98xx_i2c_driver);
kfree(container);
}
module_exit(tfa98xx_i2c_exit);
MODULE_DESCRIPTION("ASoC TFA98XX driver");
MODULE_LICENSE("GPL");