blob: c4d62a096927a7c6cd9692fa4b8e7d8d44782e96 [file] [log] [blame]
/*
* Copyright (C) 2019 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
/* vfs */
#include <linux/fs.h>
#include <asm/segment.h>
#include <linux/uaccess.h>
#include <linux/buffer_head.h>
/* alsa sound header */
#include <sound/soc.h>
/* 64bit integer */
#include <linux/math64.h>
#include "rt5509.h"
#define RT5509_CALIB_MAGIC (5526789)
static struct class *rt5509_cal_class;
static int calib_status;
enum {
RT5509_CALIB_CTRL_START = 0,
RT5509_CALIB_CTRL_DCROFFSET,
RT5509_CALIB_CTRL_N20DB,
RT5509_CALIB_CTRL_N15DB,
RT5509_CALIB_CTRL_N10DB,
RT5509_CALIB_CTRL_READOTP,
RT5509_CALIB_CTRL_READRAPP,
RT5509_CALIB_CTRL_WRITEOTP,
RT5509_CALIB_CTRL_WRITEFILE,
RT5509_CALIB_CTRL_END,
RT5509_CALIB_CTRL_ALLINONE,
RT5509_CALIB_CTRL_MAX,
};
static int rt5509_calib_get_dcroffset(struct rt5509_chip *chip)
{
struct snd_soc_codec *codec = chip->codec;
uint32_t delta_v = 0, vtemp = 0;
int ret = 0;
dev_info(codec->dev, "%s\n", __func__);
ret = snd_soc_read(codec, RT5509_REG_VTEMP_TRIM);
if (ret < 0)
return ret;
vtemp = ret & 0xffff;
ret = snd_soc_read(codec, RT5509_REG_VTHRMDATA);
if (ret < 0)
return ret;
ret &= 0xffff;
delta_v = (2730 - 400) * (ret - vtemp) / vtemp;
return delta_v;
}
static int rt5509_calib_chosen_db(struct rt5509_chip *chip, int choose)
{
struct snd_soc_codec *codec = chip->codec;
u32 data = 0;
uint8_t mode_store = 0;
int i = 0, ret = 0;
dev_info(chip->dev, "%s\n", __func__);
ret = snd_soc_read(codec, RT5509_REG_BST_MODE);
if (ret < 0)
return ret;
mode_store = ret;
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, 0x02);
if (ret < 0)
return ret;
data = 0x0080;
ret = snd_soc_write(codec, RT5509_REG_CALIB_REQ, data);
if (ret < 0)
return ret;
switch (choose) {
case RT5509_CALIB_CTRL_N20DB:
data = 0x0ccc;
break;
case RT5509_CALIB_CTRL_N15DB:
data = 0x16c3;
break;
case RT5509_CALIB_CTRL_N10DB:
data = 0x287a;
break;
default:
return -EINVAL;
}
ret = snd_soc_write(codec, RT5509_REG_CALIB_GAIN, data);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_CALIB_CTRL);
if (ret < 0)
return ret;
data = ret;
data |= 0x80;
ret = snd_soc_write(codec, RT5509_REG_CALIB_CTRL, data);
if (ret < 0)
return ret;
mdelay(120);
while (i++ < 3) {
ret = snd_soc_read(codec, RT5509_REG_CALIB_CTRL);
if (ret < 0)
return ret;
if (ret & 0x01)
break;
mdelay(20);
}
data &= ~(0x80);
ret = snd_soc_write(codec, RT5509_REG_CALIB_CTRL, data);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, mode_store);
if (ret < 0)
return ret;
if (i > 3) {
dev_err(chip->dev, "over ready count\n");
return -EINVAL;
}
return snd_soc_read(codec, RT5509_REG_CALIB_OUT0);
}
static int rt5509_calib_read_otp(struct rt5509_chip *chip)
{
struct snd_soc_codec *codec = chip->codec;
int ret = 0;
ret = snd_soc_read(codec, RT5509_REG_ISENSEGAIN);
if (ret < 0)
return ret;
ret &= 0xffffff;
return ret;
}
static int rt5509_calib_write_otp(struct rt5509_chip *chip)
{
struct snd_soc_codec *codec = chip->codec;
uint8_t mode_store = 0;
uint32_t param = chip->calib_dev.rspk;
uint32_t param_store = 0;
uint32_t bst_th = 0;
int ret = 0;
ret = snd_soc_read(codec, RT5509_REG_BST_TH1);
if (ret < 0)
return ret;
bst_th = ret;
ret = snd_soc_read(codec, RT5509_REG_BST_MODE);
if (ret < 0)
return ret;
mode_store = ret;
ret = snd_soc_write(codec, RT5509_REG_BST_TH1, 0x029b);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, 0x02);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_CALIB_DCR, param);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_OTPDIN);
ret &= 0x00ffff;
ret |= 0xc50000;
ret = snd_soc_write(codec, RT5509_REG_OTPDIN, ret);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_OTPCONF, 0x81);
if (ret < 0)
return ret;
msleep(100);
ret = snd_soc_write(codec, RT5509_REG_OTPCONF, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_update_bits(codec, RT5509_REG_BST_MODE,
0x03, mode_store);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_BST_TH1, bst_th);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_CALIB_DCR, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_OTPDIN, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_OTPCONF, 0x82);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, RT5509_REG_OTPCONF, 0x00);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_CALIB_DCR);
param_store = ret & 0xffffff;
dev_info(chip->dev, "store %08x, put %08x\n", param_store,
param);
if (param_store != param)
return -EINVAL;
ret = snd_soc_read(codec, RT5509_REG_OTPDIN);
dev_info(chip->dev, "otp_din = 0x%08x\n", ret);
if ((ret & 0xff0000) != 0xc50000)
return -EINVAL;
chip->calibrated = 1;
return 0;
}
static int rt5509_calib_rwotp(struct rt5509_chip *chip, int choose)
{
int ret = 0;
dev_info(chip->dev, "%s\n", __func__);
switch (choose) {
case RT5509_CALIB_CTRL_READOTP:
ret = rt5509_calib_read_otp(chip);
break;
case RT5509_CALIB_CTRL_WRITEOTP:
ret = rt5509_calib_write_otp(chip);
break;
default:
return -EINVAL;
}
return ret;
}
static int rt5509_calib_read_rapp(struct rt5509_chip *chip)
{
struct snd_soc_codec *codec = chip->codec;
int ret = 0;
ret = snd_soc_read(codec, RT5509_REG_RAPP);
if (ret < 0)
return ret;
ret &= 0xffffff;
return ret;
}
static int rt5509_calib_write_file(struct rt5509_chip *chip)
{
return 0;
}
static int rt5509_calib_start_process(struct rt5509_chip *chip)
{
int ret = 0;
dev_info(chip->dev, "%s\n", __func__);
ret = snd_soc_read(chip->codec, RT5509_REG_CHIPEN);
if (ret < 0)
return ret;
if (!(ret & RT5509_SPKAMP_ENMASK)) {
dev_err(chip->dev, "class D not turn on\n");
return -EINVAL;
}
ret = snd_soc_read(chip->codec, RT5509_REG_I2CBCKLRCKCONF);
if (ret < 0)
return ret;
if (ret & 0x08) {
dev_err(chip->dev, "BCK loss\n");
return -EINVAL;
}
ret = snd_soc_read(chip->codec, RT5509_REG_CALIB_REQ);
if (ret < 0)
return ret;
chip->pilot_freq = ret & 0xffff;
return 0;
}
static int rt5509_calib_end_process(struct rt5509_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return snd_soc_write(chip->codec, RT5509_REG_CALIB_REQ,
chip->pilot_freq);
}
static int rt5509_calib_trigger_read(struct rt5509_calib_classdev *cdev)
{
struct rt5509_chip *chip = container_of(cdev,
struct rt5509_chip, calib_dev);
int ret = 0;
dev_dbg(chip->dev, "%s\n", __func__);
ret = rt5509_calib_start_process(chip);
if (ret < 0) {
dev_err(chip->dev, "start fail\n");
dev_err(chip->dev, "bck not valid or amp not turn on\n");
goto out_trigger_read;
}
ret = rt5509_calib_get_dcroffset(chip);
if (ret < 0) {
cdev->dcr_offset = 0xffffffff;
goto out_trigger_read;
}
cdev->dcr_offset = ret;
dev_dbg(chip->dev, "dcr_offset -> %d\n", cdev->dcr_offset);
ret = rt5509_calib_chosen_db(chip, RT5509_CALIB_CTRL_N15DB);
if (ret < 0) {
cdev->n15db = 0xffffffff;
goto out_trigger_read;
}
cdev->n15db = ret;
dev_dbg(chip->dev, "n15db -> 0x%08x\n", cdev->n15db);
ret = rt5509_calib_rwotp(chip, RT5509_CALIB_CTRL_READOTP);
if (ret < 0) {
cdev->gsense_otp = 0xffffffff;
goto out_trigger_read;
}
cdev->gsense_otp = ret;
dev_dbg(chip->dev, "gsense_otp -> 0x%08x\n", cdev->gsense_otp);
ret = rt5509_calib_read_rapp(chip);
if (ret < 0) {
cdev->rapp = 0xffffffff;
goto out_trigger_read;
}
cdev->rapp = ret;
dev_dbg(chip->dev, "rapp -> 0x%08x\n", cdev->rapp);
return 0;
out_trigger_read:
return ret;
}
static int rt5509_calib_trigger_write(struct rt5509_calib_classdev *cdev)
{
struct rt5509_chip *chip = container_of(cdev,
struct rt5509_chip, calib_dev);
int ret = 0;
dev_dbg(chip->dev, "%s\n", __func__);
ret = rt5509_calib_rwotp(chip, RT5509_CALIB_CTRL_WRITEOTP);
if (ret < 0)
goto out_trigger_write;
ret = rt5509_calib_write_file(chip);
if (ret < 0)
goto out_trigger_write;
ret = rt5509_calib_end_process(chip);
if (ret < 0)
goto out_trigger_write;
return 0;
out_trigger_write:
return ret;
}
static int64_t rt5509_integer_dcr_calculation(int index, uint32_t n_db)
{
int64_t a = 0, x = 0;
int64_t coeffi = 0;
int i = 0;
int64_t ret = 0;
switch (index) {
case RT5509_CALIB_CTRL_N20DB:
coeffi = 81051042;
break;
case RT5509_CALIB_CTRL_N15DB:
coeffi = 25630590;
break;
case RT5509_CALIB_CTRL_N10DB:
coeffi = 8105104;
break;
default:
return -1;
}
a = n_db * coeffi;
x = 1 << 24;
for (i = 0; i < 10; i++)
x = div_s64(((x * x + a) >> 1), x);
ret = 1;
ret <<= 32;
ret *= 10000000;
return div_s64(ret, x);
}
#define RefT (-40)
#define alpha_r (265)
static int rt5509_calib_trigger_calculation(struct rt5509_calib_classdev *cdev)
{
struct rt5509_chip *chip = container_of(cdev,
struct rt5509_chip, calib_dev);
int64_t dcr_n15i = 0, dcr_i = 0;
int64_t alpha_rappi = 0, rappi = 0;
int64_t rspki = 0;
int64_t rspk_mini = 0, rspk_maxi = 0;
dev_info(chip->dev, "dcr_offset = 0x%08x\n", cdev->dcr_offset);
dev_info(chip->dev, "n15db reg = 0x%08x\n", cdev->n15db);
dev_info(chip->dev, "gsense_otp reg = 0x%08x\n", cdev->gsense_otp);
dcr_n15i = rt5509_integer_dcr_calculation(RT5509_CALIB_CTRL_N15DB,
cdev->n15db);
if (dcr_n15i < 0)
return -EINVAL;
dcr_i = dcr_n15i;
alpha_rappi = cdev->alphaspk;
rappi = cdev->rapp;
rappi <<= 9;
dev_info(chip->dev, "rappi = %llx\n", rappi);
rspki = div_s64((dcr_i * cdev->gsense_otp), 8);
rspki *= (cdev->alphaspk + 25);
rspki = div_s64(rspki, (alpha_r + RefT));
rspki = div_s64(rspki, 1048576);
dev_info(chip->dev, "pre rspki = %llx\n", rspki);
rspki -= (div_s64((rappi * (alpha_rappi + 25)), (alpha_rappi + 50)));
dev_info(chip->dev, "post rspki = %llx\n", rspki);
rspk_mini = cdev->rspkmin;
rspk_mini <<= 32;
rspk_maxi = cdev->rspkmax;
rspk_maxi <<= 32;
if ((rspki * 80) < rspk_mini || (rspki * 80) > rspk_maxi) {
dev_err(chip->dev, "rspki over range\n");
return -EINVAL;
}
rspki >>= 9;
cdev->rspk = (uint32_t)rspki;
cdev->rspk &= 0xffffff;
dev_info(chip->dev, "rspk = 0x%08x\n", cdev->rspk);
return 0;
}
void rt5509_calib_destroy(struct rt5509_chip *chip)
{
dev_dbg(chip->dev, "%s\n", __func__);
device_unregister(chip->calib_dev.dev);
}
EXPORT_SYMBOL_GPL(rt5509_calib_destroy);
int rt5509_calib_create(struct rt5509_chip *chip)
{
struct rt5509_calib_classdev *pcalib_dev = &chip->calib_dev;
int ret = 0;
dev_dbg(chip->dev, "%s\n", __func__);
ret = snd_soc_read(chip->codec, RT5509_REG_OTPDIN);
ret &= 0xff0000;
if (ret == 0xc50000)
chip->calibrated = 1;
ret = snd_soc_read(chip->codec, RT5509_REG_CALIB_DCR);
ret &= 0xffffff;
pcalib_dev->rspk = ret;
/* default rspk min,max,alphspk */
pcalib_dev->rspkmin = 10;
pcalib_dev->rspkmax = 160;
pcalib_dev->alphaspk = 265;
pcalib_dev->trigger_read = rt5509_calib_trigger_read;
pcalib_dev->trigger_write = rt5509_calib_trigger_write;
pcalib_dev->trigger_calculation = rt5509_calib_trigger_calculation;
pcalib_dev->dev = device_create(rt5509_cal_class, NULL, 0,
pcalib_dev, "rt5509.%d", chip->pdev->id);
if (IS_ERR(pcalib_dev->dev))
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(rt5509_calib_create);
static ssize_t rt_calib_dev_attr_show(struct device *,
struct device_attribute *, char *);
static ssize_t rt_calib_dev_attr_store(struct device *,
struct device_attribute *, const char *, size_t);
static struct device_attribute rt5509_dev_attrs[] = {
__ATTR(n20db, 0444, rt_calib_dev_attr_show, rt_calib_dev_attr_store),
__ATTR(n15db, 0444, rt_calib_dev_attr_show, rt_calib_dev_attr_store),
__ATTR(n10db, 0444, rt_calib_dev_attr_show, rt_calib_dev_attr_store),
__ATTR(gsense_otp, 0444, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(rapp, 0444, rt_calib_dev_attr_show, rt_calib_dev_attr_store),
__ATTR(rspk, 0664, rt_calib_dev_attr_show, rt_calib_dev_attr_store),
__ATTR(calib_data, 0444, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(dcr_offset, 0444, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(calibrated, 0444, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(chip_rev, 0444, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(rspkmin, 0644, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(rspkmax, 0644, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(alphaspk, 0644, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR(event_read, 0444, rt_calib_dev_attr_show,
rt_calib_dev_attr_store),
__ATTR_NULL,
};
static struct attribute *rt5509_cal_dev_attrs[] = {
&rt5509_dev_attrs[0].attr,
&rt5509_dev_attrs[1].attr,
&rt5509_dev_attrs[2].attr,
&rt5509_dev_attrs[3].attr,
&rt5509_dev_attrs[4].attr,
&rt5509_dev_attrs[5].attr,
&rt5509_dev_attrs[6].attr,
&rt5509_dev_attrs[7].attr,
&rt5509_dev_attrs[8].attr,
&rt5509_dev_attrs[9].attr,
&rt5509_dev_attrs[10].attr,
&rt5509_dev_attrs[11].attr,
&rt5509_dev_attrs[12].attr,
&rt5509_dev_attrs[13].attr,
NULL,
};
static const struct attribute_group rt5509_cal_group = {
.attrs = rt5509_cal_dev_attrs,
};
static const struct attribute_group *rt5509_cal_groups[] = {
&rt5509_cal_group,
NULL,
};
enum {
RT5509_CALIB_DEV_N20DB = 0,
RT5509_CALIB_DEV_N15DB,
RT5509_CALIB_DEV_N10DB,
RT5509_CALIB_DEV_GSENSE_OTP,
RT5509_CALIB_DEV_RAPP,
RT5509_CALIB_DEV_RSPK,
RT5509_CALIB_DEV_CALIB_DATA,
RT5509_CALIB_DEV_DCROFFSET,
RT5509_CALIB_DEV_CALIBRATED,
RT5509_CALIB_DEV_CHIPREV,
RT5509_CALIB_DEV_RSPKMIN,
RT5509_CALIB_DEV_RSPKMAX,
RT5509_CALIB_DEV_ALPHASPK,
RT5509_CALIB_DEV_EVENT_READ,
RT5509_CALIB_DEV_MAX,
};
static int calib_data_file_read(struct rt5509_chip *chip, char *buf)
{
return -EINVAL;
}
static int rt_dev_event_read(struct rt5509_chip *chip, char *buf)
{
struct snd_soc_codec *codec = chip->codec;
int i = 0, index = 0, ret = 0;
ret = snd_soc_read(codec, RT5509_REG_CHIPEN);
if (ret < 0)
return ret;
if (!(ret & RT5509_SPKAMP_ENMASK)) {
dev_err(chip->dev, "amp not turn on\n");
return -EINVAL;
}
index += scnprintf(buf + index, PAGE_SIZE - index,
"important reg dump ++\n");
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x03) -> 0x%02x\n", ret);
ret = snd_soc_read(codec, RT5509_REG_CHIPREV);
if (ret < 0)
return ret;
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x00) -> 0x%02x\n", ret);
ret = snd_soc_read(codec, RT5509_REG_EVENTINFO);
if (ret < 0)
return ret;
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x01) -> 0x%02x\n", ret);
ret = snd_soc_read(codec, RT5509_REG_DMGFLAG);
if (ret < 0)
return ret;
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x02) -> 0x%02x\n", ret);
ret = snd_soc_read(codec, RT5509_REG_BST_MODE);
if (ret < 0)
return ret;
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x1e) -> 0x%02x\n", ret);
ret = snd_soc_read(codec, RT5509_REG_ISENSEGAIN);
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x46) -> 0x%06x\n", ret & 0xffffff);
ret = snd_soc_read(codec, RT5509_REG_CALIB_DCR);
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x4e) -> 0x%06x\n", ret & 0xffffff);
ret = snd_soc_read(codec, RT5509_REG_VTEMP_TRIM);
if (ret < 0)
return ret;
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0xc4) -> 0x%04x\n", ret);
ret = snd_soc_read(codec, RT5509_REG_INTERRUPT);
if (ret < 0)
return ret;
index += scnprintf(buf + index, PAGE_SIZE - index,
"events -> 0x%04x\n", ret);
index += scnprintf(buf + index, PAGE_SIZE - index,
"important reg dump --\n");
index += scnprintf(buf + index, PAGE_SIZE - index,
"impedance curve ++\n");
for (i = 0x10; i <= 0x1a; i++) {
ret = snd_soc_write(codec, RT5509_REG_SPKRPTSEL, i);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_SPKRPT);
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x%02x) -> 0x%06x\n", i, ret & 0xffffff);
}
ret = snd_soc_write(codec, RT5509_REG_SPKRPTSEL, 0x0d);
if (ret < 0)
return ret;
ret = snd_soc_read(codec, RT5509_REG_SPKRPT);
index += scnprintf(buf + index, PAGE_SIZE - index,
"i(0x0d) -> 0x%06x\n", ret & 0xffffff);
index += scnprintf(buf + index, PAGE_SIZE - index,
"impedance curve --\n");
ret = index;
return ret;
}
static ssize_t rt_calib_dev_attr_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rt5509_calib_classdev *calib_dev = dev_get_drvdata(dev);
struct rt5509_chip *chip = container_of(calib_dev,
struct rt5509_chip, calib_dev);
const ptrdiff_t offset = attr - rt5509_dev_attrs;
int ret = 0;
switch (offset) {
case RT5509_CALIB_DEV_N20DB:
ret = scnprintf(buf, PAGE_SIZE, "0x%08x\n", calib_dev->n20db);
break;
case RT5509_CALIB_DEV_N15DB:
ret = scnprintf(buf, PAGE_SIZE, "0x%08x\n", calib_dev->n15db);
break;
case RT5509_CALIB_DEV_N10DB:
ret = scnprintf(buf, PAGE_SIZE, "0x%08x\n", calib_dev->n10db);
break;
case RT5509_CALIB_DEV_GSENSE_OTP:
ret = scnprintf(buf, PAGE_SIZE, "0x%08x\n",
calib_dev->gsense_otp);
break;
case RT5509_CALIB_DEV_RAPP:
ret = scnprintf(buf, PAGE_SIZE, "0x%08x\n", calib_dev->rapp);
break;
case RT5509_CALIB_DEV_RSPK:
ret = scnprintf(buf, PAGE_SIZE, "0x%08x\n", calib_dev->rspk);
break;
case RT5509_CALIB_DEV_CALIB_DATA:
ret = calib_data_file_read(chip, buf);
break;
case RT5509_CALIB_DEV_DCROFFSET:
ret = scnprintf(buf, PAGE_SIZE, "0x%08x\n",
calib_dev->dcr_offset);
break;
case RT5509_CALIB_DEV_CALIBRATED:
ret = scnprintf(buf, PAGE_SIZE, "%d\n", chip->calibrated);
break;
case RT5509_CALIB_DEV_CHIPREV:
ret = scnprintf(buf, PAGE_SIZE, "%d\n", chip->chip_rev);
break;
case RT5509_CALIB_DEV_RSPKMIN:
ret = scnprintf(buf, PAGE_SIZE, "%d\n", calib_dev->rspkmin);
break;
case RT5509_CALIB_DEV_RSPKMAX:
ret = scnprintf(buf, PAGE_SIZE, "%d\n", calib_dev->rspkmax);
break;
case RT5509_CALIB_DEV_ALPHASPK:
ret = scnprintf(buf, PAGE_SIZE, "%d\n", calib_dev->alphaspk);
break;
case RT5509_CALIB_DEV_EVENT_READ:
ret = rt_dev_event_read(chip, buf);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static ssize_t rt_calib_dev_attr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct rt5509_calib_classdev *calib_dev = dev_get_drvdata(dev);
const ptrdiff_t offset = attr - rt5509_dev_attrs;
uint32_t tmp = 0;
int32_t tmp2 = 0;
switch (offset) {
case RT5509_CALIB_DEV_RSPK:
if (sscanf(buf, "0x%08x", &tmp) != 1)
return -EINVAL;
calib_dev->rspk = tmp;
break;
case RT5509_CALIB_DEV_RSPKMIN:
if (kstrtoint(buf, 10, &tmp2) < 0)
return -EINVAL;
calib_dev->rspkmin = tmp2;
break;
case RT5509_CALIB_DEV_RSPKMAX:
if (kstrtoint(buf, 10, &tmp2) < 0)
return -EINVAL;
calib_dev->rspkmax = tmp2;
break;
case RT5509_CALIB_DEV_ALPHASPK:
if (kstrtoint(buf, 10, &tmp2) < 0)
return -EINVAL;
calib_dev->alphaspk = tmp2;
break;
default:
return -EINVAL;
}
return count;
}
static ssize_t rt_calib_class_attr_show(struct class *,
struct class_attribute *, char *);
static ssize_t rt_calib_class_attr_store(struct class *,
struct class_attribute *, const char *, size_t);
static struct class_attribute rt5509_class_attrs[] = {
__ATTR(trigger, 0220, rt_calib_class_attr_show,
rt_calib_class_attr_store),
__ATTR(status, 0444, rt_calib_class_attr_show,
rt_calib_class_attr_store),
__ATTR_NULL,
};
enum {
RT5509_CALIB_CLASS_TRIGGER = 0,
RT5509_CALIB_CLASS_STATUS,
RT5509_CALIB_CLASS_MAX,
};
static ssize_t rt_calib_class_attr_show(struct class *cls,
struct class_attribute *attr, char *buf)
{
const ptrdiff_t offset = attr - rt5509_class_attrs;
int ret = 0;
switch (offset) {
case RT5509_CALIB_CLASS_STATUS:
ret = scnprintf(buf, PAGE_SIZE, "%d\n", calib_status);
break;
case RT5509_CALIB_CLASS_TRIGGER:
default:
return -EINVAL;
}
return ret;
}
static int rt_calib_trigger_read(struct device *dev, void *data)
{
struct rt5509_calib_classdev *calib_dev = dev_get_drvdata(dev);
return calib_dev->trigger_read ?
calib_dev->trigger_read(calib_dev) : -EINVAL;
}
static int rt_calib_trigger_write(struct device *dev, void *data)
{
struct rt5509_calib_classdev *calib_dev = dev_get_drvdata(dev);
return calib_dev->trigger_write ?
calib_dev->trigger_write(calib_dev) : -EINVAL;
}
static int rt_calib_trigger_calculation(struct device *dev, void *data)
{
struct rt5509_calib_classdev *calib_dev = dev_get_drvdata(dev);
return calib_dev->trigger_calculation ?
calib_dev->trigger_calculation(calib_dev) : -EINVAL;
}
static int rt_calib_trigger_sequence(struct class *cls, int seq)
{
int ret = 0;
switch (seq) {
case RT5509_CALIB_CTRL_START:
ret = class_for_each_device(cls, NULL, NULL,
rt_calib_trigger_read);
break;
case RT5509_CALIB_CTRL_END:
ret = class_for_each_device(cls, NULL, NULL,
rt_calib_trigger_write);
break;
case RT5509_CALIB_CTRL_ALLINONE:
ret = rt_calib_trigger_sequence(cls, RT5509_CALIB_CTRL_START);
if (ret < 0) {
pr_err("%s: trigger read fail\n", cls->name);
return ret;
}
ret = class_for_each_device(cls, NULL, NULL,
rt_calib_trigger_calculation);
if (ret < 0) {
pr_err("%s: trigger calculation fail\n", cls->name);
return ret;
}
ret = rt_calib_trigger_sequence(cls, RT5509_CALIB_CTRL_END);
if (ret < 0) {
pr_err("%s: trigger write fail\n", cls->name);
return ret;
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static ssize_t rt_calib_class_attr_store(struct class *cls,
struct class_attribute *attr, const char *buf, size_t cnt)
{
const ptrdiff_t offset = attr - rt5509_class_attrs;
int parse_val = 0;
int ret = 0;
switch (offset) {
case RT5509_CALIB_CLASS_TRIGGER:
if (kstrtoint(buf, 10, &parse_val) < 0)
return -EINVAL;
parse_val -= RT5509_CALIB_MAGIC;
ret = rt_calib_trigger_sequence(cls, parse_val);
calib_status = ret;
if (ret < 0)
return ret;
break;
case RT5509_CALIB_CLASS_STATUS:
default:
return -EINVAL;
}
return cnt;
}
static int __init rt5509_cal_init(void)
{
int i = 0, ret = 0;
rt5509_cal_class = class_create(THIS_MODULE, "rt5509_cal");
if (IS_ERR(rt5509_cal_class))
return PTR_ERR(rt5509_cal_class);
for (i = 0; rt5509_class_attrs[i].attr.name; i++) {
ret = class_create_file(rt5509_cal_class,
&rt5509_class_attrs[i]);
if (ret < 0)
goto out_cal_init;
}
rt5509_cal_class->dev_groups = rt5509_cal_groups;
return 0;
out_cal_init:
while (--i >= 0)
class_remove_file(rt5509_cal_class, &rt5509_class_attrs[i]);
class_destroy(rt5509_cal_class);
return ret;
}
static void __exit rt5509_cal_exit(void)
{
int i = 0;
for (i = 0; rt5509_class_attrs[i].attr.name; i++)
class_remove_file(rt5509_cal_class, &rt5509_class_attrs[i]);
class_destroy(rt5509_cal_class);
}
subsys_initcall(rt5509_cal_init);
module_exit(rt5509_cal_exit);
MODULE_AUTHOR("CY_Huang <cy_huang@richtek.com>");
MODULE_DESCRIPTION("RT5509 SPKAMP calibration");
MODULE_LICENSE("GPL");