blob: 25da200099bbe09ecf761d67bb3033ba7837eebd [file] [log] [blame]
/*
* Base driver for Marvell MAP
*
* Copyright (C) 2014 Marvell International Ltd.
* Nenghua Cao <nhcao@marvell.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/fs.h>
#include <linux/mfd/core.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/mfd/mmp-map.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include "mmp-map-fw.h"
#define DRV_NAME "mmp-map"
#define DRIVER_VERSION "1.00"
#define DRIVER_RELEASE_DATE "Jun.17 2013"
static int map_offset, aux_offset;
static struct map_private *audio_map_priv;
static void *regmap_aux;
static bool pmic_is_88pm880;
bool buck1slp_ever_used_by_map;
#define to_clk_audio(clk) (container_of(clk, struct clk_audio, hw))
/* tdm platform data */
static struct tdm_platform_data tdm_pdata = {
#ifdef USE_3_WIRES_MODE
/* may need config USE_STATIC_SLOT_ALLOC */
.use_4_wires = 0,
#else
.use_4_wires = 1,
#endif
.slot_size = 20,
.slot_space = 1,
.start_slot = 0,
.fsyn_pulse_width = 20,
};
static struct mfd_cell sub_devs[] = {
{
.of_compatible = "marvell,mmp-map-be",
.name = "mmp-map-be",
.id = -1,
},
{
.of_compatible = "marvell,mmp-map-be-tdm",
.name = "mmp-map-be-tdm",
.platform_data = &tdm_pdata,
.pdata_size = sizeof(struct tdm_platform_data),
.id = -1,
},
{
.of_compatible = "marvell,mmp-map-codec",
.name = "mmp-map-codec",
.id = -1,
},
};
static struct regmap_config mmp_map_regmap_config = {
.name = "mmp-map",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = MAP_DAC_ANA_MISC,
.cache_type = REGCACHE_NONE,
};
#define MAP_FW "/data/log/audio/firmware"
static u32 firmware_reg;
static ssize_t firmware_update_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 val;
if (firmware_reg != 0) {
val = map_raw_read(audio_map_priv, firmware_reg);
pr_info("firmware reg 0x%x, value 0x%x\n", firmware_reg, val);
return 0;
}
pr_info("audio firmware file is in /data/log/audio/firmware\n");
return 0;
}
static int find_number_end(const char *buf)
{
int i = 0;
while (buf[0] != ' ' && buf[0] != '\n') {
buf++;
i++;
}
return i;
}
static ssize_t firmware_update_set(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
char number[20];
struct file *p;
int ret;
mm_segment_t old_fs;
char *buffer = NULL;
char *tmp_buf = NULL;
u32 addr, val;
unsigned int reg;
if (buf[0] == '0' && buf[1] == 'x') {
memset(number, '\0', 20);
memcpy(number, buf, find_number_end(buf));
ret = kstrtouint((const char *)number, 16, &addr);
firmware_reg = addr;
if (firmware_reg < 0x2000 || firmware_reg > 0x7ffc) {
pr_info("It's not a valid reg value\n");
firmware_reg = 0;
}
}
/* echo 'f' can update the firmware */
if (buf[0] != 'f')
return count;
if (audio_map_priv == NULL)
return -EINVAL;
/* currently our firmware file is 23k */
buffer = kmalloc(30000, GFP_ATOMIC);
if (buf == NULL) {
pr_err("firmware_update: %s: malloc error\n", __func__);
return -EINVAL;
}
tmp_buf = buffer;
memset(number, '\0', 20);
memset(buffer, '\0', 30000);
p = filp_open(MAP_FW, O_RDWR | O_CREAT
| O_LARGEFILE | O_APPEND, 0777);
if (IS_ERR(p)) {
pr_err("firmware file open failed\n");
return -EINVAL;
}
old_fs = get_fs();
set_fs(get_ds());
p->f_op->llseek(p, 0, 0);
ret = p->f_op->read(p, buffer, 30000, &p->f_pos);
set_fs(old_fs);
/* Load MAP DSP firmware */
reg = MAP_TOP_CTRL_REG_2;
val = map_raw_read(audio_map_priv, reg);
val &= ~(1 << 6); /* choose apb pclk */
map_raw_write(audio_map_priv, reg, val);
while (tmp_buf - buffer < p->f_pos) {
memset(number, '\0', 20);
memcpy(number, tmp_buf, find_number_end(tmp_buf));
ret = kstrtouint((const char *)number, 16, &addr);
if (ret < 0)
return ret;
tmp_buf += find_number_end(tmp_buf) + 1;
while (tmp_buf[0] == ' ' || tmp_buf[0] == '0')
tmp_buf++;
memset(number, '\0', 20);
memcpy(number, tmp_buf, find_number_end(tmp_buf));
ret = kstrtouint((const char *)number, 16, &val);
if (ret < 0)
return ret;
tmp_buf += find_number_end(tmp_buf) + 1;
while (tmp_buf[0] == ' ' || tmp_buf[0] == '0')
tmp_buf++;
map_raw_write(audio_map_priv, addr, val);
}
audio_map_priv->dsp1_sw_id =
map_raw_read(audio_map_priv, MAP_DSP1_FW_LOCATE);
audio_map_priv->dsp2_sw_id =
map_raw_read(audio_map_priv, MAP_DSP2_FW_LOCATE);
audio_map_priv->dsp1a_sw_id =
map_raw_read(audio_map_priv, MAP_DSP1A_FW_LOCATE);
val |= (1 << 6); /* choose dig_clk */
map_raw_write(audio_map_priv, reg, val);
filp_close(p, NULL);
kfree(buffer);
pr_info("audio firmware updated success\n");
return count;
}
static DEVICE_ATTR(firmware_update, 0644,
firmware_update_show, firmware_update_set);
static int create_update_firmware_file(struct platform_device *pdev)
{
int ret;
/* add firmware_update sysfs entries */
ret = device_create_file(&pdev->dev,
&dev_attr_firmware_update);
if (ret < 0)
dev_err(&pdev->dev,
"%s: failed to add firmware_update sysfs files: %d\n",
__func__, ret);
return ret;
}
static int map_32k_apll_enable(struct map_private *map_priv)
{
void __iomem *reg_addr, *dspaux_base;
u32 refdiv, post_div, vco_div, fbdec_div, fbdec_int, vco_en, div_en;
u32 ICP, ICP_DLL, CTUNE, TEST_MON, KVCO, FD_SEL, VDDL;
u32 val, time_out = 2000;
unsigned long fvco;
u32 srate = 48000;
dspaux_base = map_priv->regs_aux;
if (dspaux_base == NULL) {
pr_err("wrong audio aux base\n");
return -EINVAL;
}
/* power on audio island */
map_priv->poweron(map_priv->apmu_base, 1);
/* below value are fixed */
KVCO = 1;
ICP = 2;
FD_SEL = 1;
CTUNE = 1;
ICP_DLL = 1;
VDDL = 1;
TEST_MON = 0;
/* 32K crysto input */
refdiv = 1;
vco_en = 1;
div_en = 1;
if ((srate % 8000) == 0) {
/* 8k famliy */
fbdec_div = 0;
fbdec_int = 0xb4;
/* over-sample rate = 192 */
post_div = 0x18;
vco_div = 4;
fvco = 589824000 / vco_div;
} else if ((srate % 11025) == 0) {
/* 8k famliy */
fbdec_div = 6;
fbdec_int = 0xa5;
/* over-sample rate = 192 */
post_div = 0x18;
vco_div = 4;
fvco = 541900800 / vco_div;
} else {
pr_err("error: no pll setting for such clock!\n");
return -EINVAL;
}
/*
* 1: Assert reset for the APLL and PU: apll1_config1
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL1_CONF_1;
val = readl(reg_addr);
/* set power up, and also set reset */
val |= 3;
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 2: set ICP, REV_DIV, FBDIV_INT, FBDIV_DEC, ICP_PLL, KVCO
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL1_CONF_1;
val = readl(reg_addr);
/* clear bits: [31-2] */
val &= 3;
val |=
((KVCO << 31) | (ICP << 27) | (fbdec_div << 23) | (fbdec_int << 15)
| (refdiv << 6) | (FD_SEL << 4) | (CTUNE << 2));
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 3: Set the required POSTDIV_AUDIO value
* POSTDIV_AUDIO = 0x93(147) for 48KHz, over-sample rate 64
*/
/* 3.1: config apll1 fast clock: VCO_DIV = 1 */
reg_addr = dspaux_base + DSP_AUDIO_PLL1_CONF_4;
val = readl(reg_addr);
val &= ~(0xfff);
val |= vco_div;
writel(val, reg_addr);
val = readl(reg_addr);
/* 3.2: config apll1 slow clock */
reg_addr = dspaux_base + DSP_AUDIO_PLL1_CONF_2;
val = readl(reg_addr);
val &= (0xf << 28);
val |=
((TEST_MON << 24) | (vco_en << 23) | (post_div << 11) |
(div_en << 10) | (ICP_DLL << 5) | (0x1 << 4) | VDDL);
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 4: de-assert reset
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL1_CONF_1;
val = readl(reg_addr);
/* release reset */
val &= ~(0x1 << 1);
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 5: check DLL lock status
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL1_CONF_2;
val = readl(reg_addr);
while ((!(val & (0x1 << 28))) && time_out) {
udelay(10);
val = readl(reg_addr);
time_out--;
}
if (time_out == 0) {
pr_err("32K-PLL: DLL lock fail!\n");
return -EBUSY;
}
/*
* 6: check PLL lock status
*/
time_out = 2000;
while ((!(val & (0x1 << 29))) && time_out) {
udelay(10);
val = readl(reg_addr);
time_out--;
}
if (time_out == 0) {
pr_err("32K-PLL: PLL lock fail!\n");
return -EBUSY;
}
return 0;
}
static int map_32k_apll_disable(struct map_private *map_priv)
{
void __iomem *reg_addr, *dspaux_base;
u32 val;
dspaux_base = map_priv->regs_aux;
reg_addr = dspaux_base + DSP_AUDIO_PLL1_CONF_1;
val = readl(reg_addr);
/* reset & power off */
val &= ~0x1;
val |= (0x1 << 1);
writel(val, reg_addr);
/* power off audio island */
map_priv->poweron(map_priv->apmu_base, 0);
return 0;
}
static int map_26m_apll_enable(struct map_private *map_priv)
{
void __iomem *reg_addr, *dspaux_base;
u32 refdiv, post_div, vco_div, fbdiv, freq_off;
u32 vco_en, vco_div_en, post_div_en, val;
u32 ICP, CTUNE, TEST_MON, FD_SEL, CLK_DET_EN, INTPI, PI_EN;
u32 time_out = 2000;
u32 srate = 48000;
unsigned long fvco;
dspaux_base = map_priv->regs_aux;
if (dspaux_base == NULL) {
pr_err("wrong audio aux base\n");
return -EINVAL;
}
/* power on audio island */
map_priv->poweron(map_priv->apmu_base, 1);
/* below value are fixed */
ICP = 6;
FD_SEL = 1;
CTUNE = 1;
TEST_MON = 0;
INTPI = 2;
CLK_DET_EN = 1;
PI_EN = 1;
/* 26M clock input */
refdiv = 6;
vco_en = 1;
vco_div_en = 1;
post_div_en = 1;
if ((srate % 8000) == 0) {
/* 8k famliy */
fbdiv = 34;
freq_off = 0x1b5;
/* over-sample rate = 192 */
post_div = 0x6;
vco_div = 4;
fvco = 589824000 / vco_div;
} else if ((srate % 11025) == 0) {
/* 8k famliy */
fbdiv = 31;
freq_off = 0x1169;
/* over-sample rate = 192 */
post_div = 0x6;
vco_div = 4;
fvco = 541900800 / vco_div;
} else {
pr_err("error: no pll setting for such clock!\n");
return -EINVAL;
}
/*
* 1: power up and reset pll
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_1;
val = readl(reg_addr);
/* set power up, and also set reset */
val |= 3;
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 2: set ICP, REV_DIV, FBDIV_IN, FBDIV_DEC, ICP_PLL, KVCO
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_1;
val = readl(reg_addr);
val &= 3;
val |=
((ICP << 27) | (fbdiv << 18) | (refdiv << 9) | (CLK_DET_EN << 8) |
(INTPI << 6) | (FD_SEL << 4) | (CTUNE << 2));
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 3: enable clk_vco
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_3;
val = readl(reg_addr);
val &= ~(0x7ff << 14);
val |=
((vco_div_en << 24) | (vco_div << 15) | (vco_en << 14) |
(TEST_MON << 0));
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 4: enable clk_audio
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_2;
val = readl(reg_addr);
val &= ~((0x7fffff << 4) | 0xf);
val |=
((post_div << 20) | (freq_off << 4) | (post_div_en << 0x1) |
(PI_EN << 0));
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 5: de-assert reset
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_1;
val = readl(reg_addr);
/* release reset */
val &= ~(0x1 << 1);
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 6: apply freq_offset_valid: wait 50us according to DE
*/
udelay(50);
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_2;
val = readl(reg_addr);
val |= (1 << 2);
writel(val, reg_addr);
val = readl(reg_addr);
/*
* 7: check PLL lock status
*/
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_1;
val = readl(reg_addr);
while (!(val & (0x1 << 31)) && time_out) {
udelay(10);
val = readl(reg_addr);
time_out--;
}
if (time_out == 0) {
pr_err("26M-PLL: PLL lock fail!\n");
return -EBUSY;
}
return 0;
}
static int map_26m_apll_disable(struct map_private *map_priv)
{
void __iomem *reg_addr, *dspaux_base;
u32 val;
dspaux_base = map_priv->regs_aux;
if (dspaux_base == NULL) {
pr_err("wrong audio aux base\n");
return -EINVAL;
}
reg_addr = dspaux_base + DSP_AUDIO_PLL2_CONF_1;
val = readl(reg_addr);
/* reset & power off */
val &= ~0x1;
val |= (0x1 << 1);
writel(val, reg_addr);
/* power off audio island */
map_priv->poweron(map_priv->apmu_base, 0);
return 0;
}
/* set port frame clock frequence */
void map_set_port_freq(struct map_private *map_priv, enum mmp_map_port port,
unsigned int rate)
{
unsigned int val = 0, reg, value;
unsigned int mask = 0;
/* sample rate */
switch (rate) {
case 8000:
val = MAP_SAMPLE_RATE_8000;
break;
case 11025:
val = MAP_SAMPLE_RATE_11025;
break;
case 16000:
val = MAP_SAMPLE_RATE_16000;
break;
case 22050:
val = MAP_SAMPLE_RATE_22050;
break;
case 32000:
val = MAP_SAMPLE_RATE_32000;
break;
case 44100:
val = MAP_SAMPLE_RATE_44100;
break;
case 48000:
val = MAP_SAMPLE_RATE_48000;
break;
default:
return;
}
reg = MAP_LRCLK_RATE_REG;
value = map_raw_read(map_priv, reg);
mask = 0xf << ((I2S_OUT - port) * 0x4);
value &= (~mask);
value |= (val << ((I2S_OUT - port) * 0x4));
map_raw_write(map_priv, reg, value);
return;
}
EXPORT_SYMBOL(map_set_port_freq);
void map_reset_port(struct map_private *map_priv, enum mmp_map_port port)
{
unsigned int reg, val = 0;
switch (port) {
case I2S1:
/* reset i2s1 interface(audio) */
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= (I2S1_RESET | ASRC1_RESET);
map_raw_write(map_priv, reg, val);
/* out of reset */
val &= ~(I2S1_RESET | ASRC1_RESET);
map_raw_write(map_priv, reg, val);
break;
case I2S2:
/* reset i2s2 interface(audio) */
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= (I2S2_RESET | ASRC2_RESET);
map_raw_write(map_priv, reg, val);
/* out of reset */
val &= ~(I2S2_RESET | ASRC2_RESET);
map_raw_write(map_priv, reg, val);
break;
case I2S3:
/* reset i2s3 interface(audio) */
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= (I2S3_RESET | ASRC3_RESET);
map_raw_write(map_priv, reg, val);
/* out of reset */
val &= ~(I2S3_RESET | ASRC3_RESET);
map_raw_write(map_priv, reg, val);
break;
case I2S4:
/* reset i2s4 interface (hifi) */
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= (I2S4_RESET | ASRC4_RESET);
map_raw_write(map_priv, reg, val);
/* out of reset */
val &= ~(I2S4_RESET | ASRC4_RESET);
map_raw_write(map_priv, reg, val);
break;
case I2S_OUT:
/* reset dei2s interface */
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= I2S_OUT_RESET;
map_raw_write(map_priv, reg, val);
/* out of reset */
val &= ~I2S_OUT_RESET;
map_raw_write(map_priv, reg, val);
break;
default:
return;
}
return;
}
EXPORT_SYMBOL(map_reset_port);
/* apply the change */
void map_apply_change(struct map_private *map_priv)
{
unsigned int val, reg;
reg = MAP_DAC_ANA_MISC;
val = APPLY_CHANGES;
map_raw_write(map_priv, reg, val);
return;
}
EXPORT_SYMBOL(map_apply_change);
int map_raw_write(struct map_private *map_priv, unsigned int reg,
unsigned int value)
{
writel(value, map_priv->regs_map + reg);
return 0;
}
EXPORT_SYMBOL(map_raw_write);
int map_raw_bulk_write(struct map_private *map_priv, unsigned int reg,
void *val, int val_count)
{
unsigned int ival, i;
for (i = 0; i < val_count; i++) {
ival = *(u32 *)(val + (i * 32));
writel(ival, map_priv->regs_map + reg);
}
return 0;
}
EXPORT_SYMBOL(map_raw_bulk_write);
unsigned int map_raw_read(struct map_private *map_priv,
unsigned int reg)
{
unsigned int value;
value = readl(map_priv->regs_map + reg);
return value;
}
EXPORT_SYMBOL(map_raw_read);
unsigned int map_raw_bulk_read(struct map_private *map_priv,
unsigned int reg, void *val, int val_count)
{
unsigned int i;
for (i = 0; i < val_count; i++) {
*(u32 *)(val + (i * 32))
= readl(map_priv->regs_map + reg + (i * 4));
}
return 0;
}
EXPORT_SYMBOL(map_raw_bulk_read);
/*
* Fixme: heat on pxa1928 A0 and helan2 Z1: if want to use dsp1a,
* the dsp1 must be enabled first
*/
DEFINE_SPINLOCK(dsp1_en_lock);
DEFINE_SPINLOCK(dsp2_en_lock);
DEFINE_SPINLOCK(dsp1a_en_lock);
static void mmp_map_dsp_mute(struct map_private *map_priv, int port,
int mute, int do_enable, int do_mute)
{
unsigned int enable_reg, mute_reg, val, mask;
unsigned int reg;
bool mono_en = false;
if (port == 1) {
enable_reg = MAP_DSP1_DAC_CTRL_REG;
mute_reg = MAP_DSP1_DAC_PROCESSING_REG;
/* reset dsp1 */
if ((mute == 0) && do_enable) {
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= (1 << 8);
map_raw_write(map_priv, reg, val);
mask = 1 << 8;
val &= (~mask);
map_raw_write(map_priv, reg, val);
if (map_priv->dsp1_mono_en)
mono_en = true;
}
} else if (port == 2) {
enable_reg = MAP_DSP2_DAC_CTRL_REG;
mute_reg = MAP_DSP2_DAC_PROCESSING_REG;
/* reset dsp1 */
if ((mute == 0) && do_enable) {
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= (1 << 9);
map_raw_write(map_priv, reg, val);
mask = 1 << 9;
val &= (~mask);
map_raw_write(map_priv, reg, val);
if (map_priv->dsp2_mono_en)
mono_en = true;
}
} else if (port == 3) {
enable_reg = MAP_ADC_CTRL_REG;
mute_reg = MAP_ADC_PROCESSING_REG;
/* reset dsp1 */
if ((mute == 0) && do_enable) {
reg = MAP_TOP_CTRL_REG_1;
val = map_raw_read(map_priv, reg);
val |= (1 << 10);
map_raw_write(map_priv, reg, val);
mask = 1 << 10;
val &= (~mask);
map_raw_write(map_priv, reg, val);
if (map_priv->dsp1a_mono_en)
mono_en = true;
}
} else
return;
if (mute) {
/* mute dsp */
if (do_mute) {
val = map_raw_read(map_priv, mute_reg);
mask = 0x20;
val &= (~mask);
map_raw_write(map_priv, mute_reg, val);
/*
* Fixme: Per DE's suggestion, add two sample delay here
* to make sure unmute is done.
*/
udelay(42);
}
/* disable dsp */
if (do_enable) {
val = map_raw_read(map_priv, enable_reg);
if (!map_priv->b0_fix)
mask = 0x13;
else
mask = 0x3;
val &= (~mask);
map_raw_write(map_priv, enable_reg, val);
}
} else {
/* enable dsp */
if (do_enable) {
val = map_raw_read(map_priv, enable_reg);
if (mono_en && !map_priv->b0_fix)
val |= 0x10;
if (!map_priv->b0_fix)
mask = 0x13;
else
mask = 0x3;
val &= (~mask);
val |= 0x3;
map_raw_write(map_priv, enable_reg, val);
}
/* unmute dsp */
if (do_mute) {
val = map_raw_read(map_priv, mute_reg);
val |= 0x20;
map_raw_write(map_priv, mute_reg, val);
}
}
}
static int mmp_map_add_port(enum mmp_map_be_port *dsp_user_port,
enum mmp_map_be_port port)
{
int i = 0, j = 0;
bool found_location = false;
for (i = 0; i < AUX; i++) {
if ((dsp_user_port[i] == NO_BE_CONN) && !found_location) {
j = i;
found_location = true;
}
/* this port has been recorded */
if (dsp_user_port[i] == port)
return 0;
}
dsp_user_port[j] = port;
return 0;
}
static int mmp_map_remove_port(enum mmp_map_be_port *dsp_user_port,
enum mmp_map_be_port port)
{
int i = 0, j = 0;
for (i = 0; i < AUX; i++) {
if (dsp_user_port[i] == port) {
j = i;
break;
}
}
/* re-arrange the array */
for (i = j; i < (AUX - 1); i++)
dsp_user_port[i] = dsp_user_port[i + 1];
dsp_user_port[AUX - 1] = NO_BE_CONN;
return 0;
}
static int mmp_map_dsp_user_count(enum mmp_map_be_port *dsp_user_port)
{
int i = 0;
for (i = 0; i < AUX; i++) {
if (dsp_user_port[i] == NO_BE_CONN)
break;
}
return i;
}
void mmp_map_dsp1_mute(struct map_private *map_priv, int mute,
enum mmp_map_be_port port)
{
spin_lock(&dsp1_en_lock);
if (mute)
mmp_map_remove_port(map_priv->dsp1_user_port, port);
else
mmp_map_add_port(map_priv->dsp1_user_port, port);
if (mute && !map_priv->dsp1_mute &&
!mmp_map_dsp_user_count(map_priv->dsp1_user_port)) {
/* mute dsp */
mmp_map_dsp_mute(map_priv, 1, mute, 0, 1);
if (map_priv->dsp1_en_cnt == 1)
mmp_map_dsp_mute(map_priv, 1, mute, 1, 0);
map_priv->dsp1_en_cnt--;
map_priv->dsp1_mute = true;
} else if (!mute && map_priv->dsp1_mute) {
if (map_priv->dsp1_en_cnt == 0)
mmp_map_dsp_mute(map_priv, 1, mute, 1, 0);
mmp_map_dsp_mute(map_priv, 1, mute, 0, 1);
map_priv->dsp1_en_cnt++;
map_priv->dsp1_mute = false;
}
spin_unlock(&dsp1_en_lock);
}
EXPORT_SYMBOL(mmp_map_dsp1_mute);
void mmp_map_dsp2_mute(struct map_private *map_priv, int mute,
enum mmp_map_be_port port)
{
spin_lock(&dsp2_en_lock);
if (mute)
mmp_map_remove_port(map_priv->dsp2_user_port, port);
else
mmp_map_add_port(map_priv->dsp2_user_port, port);
if (mute && !map_priv->dsp2_mute &&
!mmp_map_dsp_user_count(map_priv->dsp2_user_port)) {
/* mute dsp */
mmp_map_dsp_mute(map_priv, 2, mute, 1, 1);
map_priv->dsp2_mute = true;
} else if (!mute && map_priv->dsp2_mute) {
mmp_map_dsp_mute(map_priv, 2, mute, 1, 1);
map_priv->dsp2_mute = false;
}
spin_unlock(&dsp2_en_lock);
}
EXPORT_SYMBOL(mmp_map_dsp2_mute);
void mmp_map_dsp1a_mute(struct map_private *map_priv, int mute,
enum mmp_map_be_port port)
{
spin_lock(&dsp1a_en_lock);
if (mute)
mmp_map_remove_port(map_priv->dsp1a_user_port, port);
else
mmp_map_add_port(map_priv->dsp1a_user_port, port);
spin_unlock(&dsp1a_en_lock);
if (mute && !map_priv->dsp1a_mute &&
!mmp_map_dsp_user_count(map_priv->dsp1a_user_port)) {
/* mute dsp */
spin_lock(&dsp1a_en_lock);
mmp_map_dsp_mute(map_priv, 3, mute, 1, 1);
map_priv->dsp1a_mute = true;
spin_unlock(&dsp1a_en_lock);
/* will finally remove below WA */
if (!map_priv->b0_fix) {
/* disable dsp1 */
spin_lock(&dsp1_en_lock);
if (map_priv->dsp1_en_cnt == 1)
mmp_map_dsp_mute(map_priv, 1, mute, 1, 0);
map_priv->dsp1_en_cnt--;
spin_unlock(&dsp1_en_lock);
}
} else if (!mute && map_priv->dsp1a_mute) {
if (!map_priv->b0_fix) {
spin_lock(&dsp1_en_lock);
if (map_priv->dsp1_en_cnt == 0)
mmp_map_dsp_mute(map_priv, 1, mute, 1, 0);
map_priv->dsp1_en_cnt++;
spin_unlock(&dsp1_en_lock);
}
spin_lock(&dsp1a_en_lock);
mmp_map_dsp_mute(map_priv, 3, mute, 1, 1);
map_priv->dsp1a_mute = false;
spin_unlock(&dsp1a_en_lock);
}
}
EXPORT_SYMBOL(mmp_map_dsp1a_mute);
/*
* switch clock source between apll & vctcxo
* vctcxo: 0, choose apll; 1, choose vctcxo
*/
static int map_clk_src_switch(struct map_private *map_priv, u32 vctcxo)
{
u32 val, bit_sram, bit_apb;
void __iomem *reg_addr, *regs_aux, *regs_apmu;
/*
* map-lite doesn't have SRAM & APB reg in DSPAUX, and apb & sram
* clock run freely if power on is executed
*/
if (map_priv->map_lite)
return 0;
regs_aux = map_priv->regs_aux;
regs_apmu = map_priv->regs_apmu;
bit_sram = map_priv->bit_sram;
bit_apb = map_priv->bit_apb;
if (!vctcxo) {
/* restore dsp aux registers */
reg_addr = regs_aux + DSP_AUDIO_SRAM_CLK;
val = map_priv->sram;
writel(val, reg_addr);
val = readl(reg_addr);
while (!(val & 0x6))
val = readl(reg_addr);
reg_addr = regs_aux + DSP_AUDIO_APB_CLK;
val = map_priv->apb;
writel(val, reg_addr);
val = readl(reg_addr);
while (!(val & 0x3))
val = readl(reg_addr);
/* switch map_apb & sram clock source to apll */
if (map_priv->apll == APLL_32K) {
reg_addr = regs_apmu;
val = readl(reg_addr);
val &= ~((1 << bit_sram) | (1 << bit_apb));
writel(val, reg_addr);
}
} else {
/* switch map_apb & sram clock source to vctcxo */
reg_addr = regs_apmu;
val = readl(reg_addr);
val |= ((1 << bit_sram) | (1 << bit_apb));
writel(val, reg_addr);
reg_addr = regs_aux + DSP_AUDIO_SRAM_CLK;
val = readl(reg_addr);
val &= ~0xf;
writel(val, reg_addr);
reg_addr = regs_aux + DSP_AUDIO_APB_CLK;
val = readl(reg_addr);
val &= ~0x3;
writel(val, reg_addr);
}
return 0;
}
/*
* save dsp aux register and disable peripheral clock
*/
static int map_save_aux_reg(struct map_private *map_priv)
{
u32 val;
void __iomem *reg_addr, *regs_aux;
regs_aux = map_priv->regs_aux;
/* save dsp aux register */
reg_addr = regs_aux + DSP_AUDIO_ADMA_CLK;
val = readl(reg_addr);
map_priv->adma = val;
reg_addr = regs_aux + DSP_AUDIO_SSPA1_CLK;
val = readl(reg_addr);
map_priv->sspa1 = val;
reg_addr = regs_aux + DSP_AUDIO_WAKEUP_MASK;
val = readl(reg_addr);
map_priv->wakeup = val;
reg_addr = regs_aux + DSP_AUDIO_MAP_CONF;
val = readl(reg_addr);
map_priv->conf = val;
/* map-lite doesn't have below reg */
if (!map_priv->map_lite) {
reg_addr = regs_aux + DSP_AUDIO_SSPA2_CLK;
val = readl(reg_addr);
map_priv->sspa2 = val;
reg_addr = regs_aux + DSP_AUDIO_SRAM_CLK;
val = readl(reg_addr);
map_priv->sram = val;
reg_addr = regs_aux + DSP_AUDIO_APB_CLK;
val = readl(reg_addr);
map_priv->apb = val;
}
map_clk_src_switch(map_priv, 1);
return 0;
}
static int map_restore_aux_reg(struct map_private *map_priv)
{
u32 val = 0;
void __iomem *reg_addr, *regs_aux;
regs_aux = map_priv->regs_aux;
/* restore dsp aux registers */
reg_addr = regs_aux + DSP_AUDIO_ADMA_CLK;
val = map_priv->adma;
writel(val, reg_addr);
reg_addr = regs_aux + DSP_AUDIO_SSPA1_CLK;
val = map_priv->sspa1;
writel(val, reg_addr);
/* map-lite doesn't have sspa2 reg */
if (!map_priv->map_lite) {
reg_addr = regs_aux + DSP_AUDIO_SSPA2_CLK;
val = map_priv->sspa2;
writel(val, reg_addr);
}
reg_addr = regs_aux + DSP_AUDIO_WAKEUP_MASK;
val = map_priv->wakeup;
writel(val, reg_addr);
reg_addr = regs_aux + DSP_AUDIO_MAP_CONF;
val = map_priv->conf;
writel(val, reg_addr);
map_clk_src_switch(map_priv, 0);
return 0;
}
static int map_aux_clk_init(struct map_private *map_priv)
{
void __iomem *reg_addr, *regs_aux;
u32 val = 0;
regs_aux = map_priv->regs_aux;
/*
* MAP ADMA clock:
* divider: 0x1
* clock source:
* 0x00, 32k-apll slow clock
* 0x01, 26m-apll slow clock
* clock enable, out of reset
*/
reg_addr = regs_aux + DSP_AUDIO_ADMA_CLK;
if (map_priv->apll == APLL_32K)
val = 0x13;
else if (map_priv->apll == APLL_26M)
val = 0x17;
writel(val, reg_addr);
/*
* SSPA1 clock:
* bypass div: 0x1, bypass its own divider
* NOM/DENOM: default value(not used)
* clock source:
* 0x00, 32k-apll slow clock
* 0x01, 26m-apll slow clock
* clock enable, out of reset
*/
reg_addr = regs_aux + DSP_AUDIO_SSPA1_CLK;
val = readl(reg_addr);
val &= ~0x3;
if (map_priv->apll == APLL_32K)
val |= 0x8000000c;
else if (map_priv->apll == APLL_26M)
val |= 0x8000000d;
writel(val, reg_addr);
/*
* SSPA2 clock:
* bypass div: 0x1, bypass its own divider
* NOM/DENOM: default value(not used)
* clock source:
* 0x00, 32k-apll slow clock
* 0x01, 26m-apll slow clock
* clock source: 0x00, 32k-apll slow clock
*/
/* map-lite doesn't have below reg */
if (!map_priv->map_lite) {
reg_addr = regs_aux + DSP_AUDIO_SSPA2_CLK;
val = readl(reg_addr);
val &= ~0x3;
if (map_priv->apll == APLL_32K)
val |= 0x8000000c;
else if (map_priv->apll == APLL_26M)
val |= 0x8000000d;
writel(val, reg_addr);
}
/*
* MAP wake up source
* only allow ADMA interrupt will wake up system from low power.
* disable sspa1/2, map interrupt wake up.
* ADMA: bit 31~28
* SSPA: bit 15~14
* MAP: bit 13
*/
reg_addr = regs_aux + DSP_AUDIO_WAKEUP_MASK;
val = readl(reg_addr);
val &= ~(0xf << 28);
val |= (0x7 << 13);
writel(val, reg_addr);
/*
* MAP clock:
* clock source:
* 0x0, 26m-apll fast clock
* 0x1, 32k-apll fast clock
* other bits are not configured here
*/
reg_addr = regs_aux + DSP_AUDIO_MAP_CONF;
val = readl(reg_addr);
if (map_priv->apll == APLL_32K)
val |= (0x1 << 8);
else if (map_priv->apll == APLL_26M)
val &= ~(0x1 << 8);
writel(val, reg_addr);
/* I2S_SYSCLK_1, I2S_SYSCLK_2 are not used */
/*
* MAP SRAM clock:
* divider: 0x1
* clock source: vctcxo is chosen by APMU register
* 0x00, 32k-apll slow clock
* 0x01, 32k-apll fast clock
* clock enable, out of reset
*/
map_priv->sram = 0xf;
/*
* MAP APB clock:
* divider: 0x1
* clock source: the same as sram-clk
* clock enable, out of reset
*/
map_priv->apb = 0x7;
/* switch map_apb & sram clock source to apll */
map_clk_src_switch(map_priv, 0);
return 0;
}
static int map_internal_clk_init(struct map_private *map_priv)
{
unsigned int val, reg;
/* Load MAP DSP firmware */
reg = MAP_TOP_CTRL_REG_2;
val = map_raw_read(map_priv, reg);
val &= ~0x7f;
val |= (0x4d | (map_priv->pll_sel << 1));
map_raw_write(map_priv, reg, val);
return 0;
}
void map_load_dsp_pram(struct map_private *map_priv)
{
int i;
u32 off;
for (i = 0; i < ARRAY_SIZE(map_pram); i++) {
off = map_pram[i].addr;
map_raw_write(map_priv, off, map_pram[i].val);
}
map_priv->dsp1_sw_id =
map_raw_read(map_priv, MAP_DSP1_FW_LOCATE);
map_priv->dsp2_sw_id =
map_raw_read(map_priv, MAP_DSP2_FW_LOCATE);
map_priv->dsp1a_sw_id =
map_raw_read(map_priv, MAP_DSP1A_FW_LOCATE);
}
static void map_reg_dump_all(void)
{
unsigned int i, reg_val = 0;
for (i = 0; i <= 0x8c; i += 4) {
reg_val = map_raw_read(audio_map_priv, i);
pr_info("%3x = %x\n", i, reg_val);
}
for (i = 0x100; i <= 0x1b4; i += 4) {
reg_val = map_raw_read(audio_map_priv, i);
pr_info("%3x = %x\n", i, reg_val);
}
for (i = 0x200; i <= 0x2b4; i += 4) {
reg_val = map_raw_read(audio_map_priv, i);
pr_info("%3x = %x\n", i, reg_val);
}
for (i = 0x300; i <= 0x35c; i += 4) {
reg_val = map_raw_read(audio_map_priv, i);
pr_info("%3x = %x\n", i, reg_val);
}
for (i = 0x400; i <= 0x40c; i += 4) {
reg_val = map_raw_read(audio_map_priv, i);
pr_info("%3x = %x\n", i, reg_val);
}
}
static ssize_t map_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned int reg_val = 0;
size_t len = 0;
if (map_offset == 0xfff) {
map_reg_dump_all();
len = sprintf(buf, "dump all registers\n");
return len;
} else if (map_offset > 0x40c) {
pr_info("map_offset 0x%x is invalid\n", map_offset);
return -EINVAL;
}
reg_val = map_raw_read(audio_map_priv, map_offset);
len = sprintf(buf, "map_offset=0x%x, val=0x%x\n", map_offset, reg_val);
return len;
}
static ssize_t map_reg_set(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int reg_val, reg, val;
char index[20], *val_str;
/* write register: find whether it has blank */
val_str = strchr(buf, 32);
if (val_str) {
/* set the register index */
memset(index, 0, ARRAY_SIZE(index));
memcpy(index, buf, (val_str - buf));
if (kstrtouint(index, 16, &map_offset) < 0)
return -EINVAL;
if (map_offset > 0x40c) {
pr_info("map_offset 0x%x is invalid\n", map_offset);
return -EINVAL;
}
memset(index, 0, ARRAY_SIZE(index));
memcpy(index, val_str + 1, (strlen(val_str) - 1));
if (kstrtouint(index, 16, &reg_val) < 0)
return -EINVAL;
reg_val &= 0xFFFFFFFF;
map_raw_write(audio_map_priv, map_offset, reg_val);
pr_info("map_offset is 0x%x val 0x%x\n", map_offset, reg_val);
/* apply changes */
reg = MAP_DAC_ANA_MISC;
val = map_raw_read(audio_map_priv, reg);
val |= APPLY_CHANGES;
map_raw_write(audio_map_priv, reg, val);
} else {
if (kstrtouint(buf, 16, &map_offset) < 0)
return -EINVAL;
if (map_offset == 0xfff) {
map_reg_dump_all();
return count;
}
if (map_offset > 0x40c) {
pr_info("map_offset 0x%x is invalid\n", map_offset);
return -EINVAL;
}
pr_info("map_offset is 0x%x\n", map_offset);
}
return count;
}
static void dspaux_reg_dump_all(void)
{
unsigned int i, reg_val = 0;
for (i = 0; i <= 0x2c; i += 4) {
reg_val = readl(regmap_aux + i);
pr_info("%2x = %x\n", i, reg_val);
}
for (i = 0x68; i <= 0x80; i += 4) {
reg_val = readl(regmap_aux + i);
pr_info("%2x = %x\n", i, reg_val);
}
}
static ssize_t dspaux_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned int reg_val = 0;
size_t len = 0;
if (aux_offset == 0xfff) {
dspaux_reg_dump_all();
len = sprintf(buf, "dump all registers\n");
return len;
} else if (aux_offset > 0x80) {
pr_info("aux_offset 0x%x is invalid\n", map_offset);
return -EINVAL;
}
reg_val = readl(regmap_aux + aux_offset);
len = sprintf(buf, "aux_offset=0x%x, val=0x%x\n", aux_offset, reg_val);
return len;
}
static ssize_t dspaux_reg_set(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int reg_val;
char index[20], *val_str;
/* write register: find whether it has blank */
val_str = strchr(buf, 32);
if (val_str) {
/* set the register index */
memset(index, 0, ARRAY_SIZE(index));
memcpy(index, buf, (val_str - buf));
if (kstrtouint(index, 16, &aux_offset) < 0)
return -EINVAL;
if (aux_offset > 0x80) {
pr_info("aux_offset 0x%x is invalid\n", map_offset);
return -EINVAL;
}
memset(index, 0, ARRAY_SIZE(index));
memcpy(index, val_str + 1, (strlen(val_str) - 1));
if (kstrtouint(index, 16, &reg_val) < 0)
return -EINVAL;
reg_val &= 0xFFFFFFFF;
writel(reg_val, regmap_aux + aux_offset);
pr_info("aux_offset is 0x%x val 0x%x\n", aux_offset, reg_val);
} else {
if (kstrtouint(buf, 16, &aux_offset) < 0)
return -EINVAL;
if (aux_offset == 0xfff) {
dspaux_reg_dump_all();
return count;
}
if (aux_offset > 0x80) {
pr_info("aux_offset 0x%x is invalid\n", aux_offset);
return -EINVAL;
}
pr_info("aux_offset is 0x%x\n", aux_offset);
}
return count;
}
static DEVICE_ATTR(map_reg, 0644, map_reg_show, map_reg_set);
static DEVICE_ATTR(dspaux_reg, 0644, dspaux_reg_show, dspaux_reg_set);
static int map_config(struct map_private *map_priv)
{
unsigned int val, reg;
/* configure pad */
#ifdef USE_3_WIRES_MODE
/* set 3 wires mode in MAP */
val = readl(map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
val |= (1 << 10);
writel(val, map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
#endif
val = readl(map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
val &= ~MAP_RESET;
writel(val, map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
val = readl(map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
val |= MAP_RESET;
writel(val, map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
#ifndef CONFIG_MAP_BYPASS
val = readl(map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
val &= ~(SSPA1_PAD_OUT_SRC_MASK | SSPA2_PAD_OUT_SRC_MASK);
val |= TDM_DRIVE_SSPA1_PAD;
val |= I2S4_DRIVE_SSPA2_PAD;
val &= ~AUD_SSPA1_INPUT_SRC;
val &= ~AUD_SSPA2_INPUT_SRC;
val |= CP_GSSP_INPUT_FROM_I2S2;
writel(val, map_priv->regs_aux + DSP_AUDIO_MAP_CONF);
#endif
/* Load MAP DSP firmware */
reg = MAP_TOP_CTRL_REG_2;
val = map_raw_read(map_priv, reg);
val &= ~(1 << 6); /* choose apb pclk */
map_raw_write(map_priv, reg, val);
map_load_dsp_pram(map_priv);
val |= (1 << 6); /* choose dig_clk */
map_raw_write(map_priv, reg, val);
return 0;
}
void map_set_sleep_vol(struct map_private *map_priv, int on)
{
int ret = 0;
if (map_priv->sleep_vol <= 0)
return;
/* enable audio mode for all audio scenarios*/
if (on) {
if (pmic_is_88pm880) {
if (!map_priv->vccmain) {
map_priv->vccmain =
regulator_get(map_priv->dev,
"vccmain");
ret = regulator_set_voltage(map_priv->vccmain,
map_priv->sleep_vol,
map_priv->sleep_vol);
if (ret) {
pr_err("%s: set vccmain fails: %d\n",
__func__, ret);
map_priv->vccmain = NULL;
return;
}
}
} else {
regulator_set_suspend_mode(map_priv->vccmain,
REGULATOR_MODE_IDLE);
}
} else {
if (pmic_is_88pm880) {
if (map_priv->vccmain) {
regulator_put(map_priv->vccmain);
buck1slp_ever_used_by_map = true;
map_priv->vccmain = NULL;
}
return;
} else {
if (!map_priv->vccmain)
pr_info("please set regulator vccmain.\n");
else
regulator_set_suspend_mode(map_priv->vccmain,
REGULATOR_MODE_NORMAL);
}
}
}
/* put map into active state*/
void map_be_active(struct map_private *map_priv)
{
unsigned int reg, val;
bool map_is_active = false;
spin_lock(&map_priv->map_lock);
if (!map_priv->user_count) {
/* put map into active state */
reg = MAP_TOP_CTRL_REG_1;
val = 0x3;
map_raw_write(map_priv, reg, val);
/* set clock here */
reg = MAP_TOP_CTRL_REG_2;
val = (0x4d | (map_priv->pll_sel << 1));
map_raw_write(map_priv, reg, val);
reg = MAP_ASRC_CTRL_REG;
val = 0xff;
map_raw_write(map_priv, reg, val);
map_is_active = true;
}
map_priv->user_count++;
spin_unlock(&map_priv->map_lock);
if (map_is_active) {
/* get constrain out of spinlock since it may sleep */
if (map_priv->lpm_qos >= 0)
pm_qos_update_request(&map_priv->qos_idle, map_priv->lpm_qos);
/* set vol should out of spinlock status, it may sleep */
map_set_sleep_vol(map_priv, 1);
}
return;
}
EXPORT_SYMBOL(map_be_active);
/* put map into reset state*/
void map_be_reset(struct map_private *map_priv)
{
unsigned int reg, val;
bool map_is_reset = false;
spin_lock(&map_priv->map_lock);
map_priv->user_count--;
if (!map_priv->user_count) {
/* reset map */
reg = MAP_TOP_CTRL_REG_1;
val = 0xfff10;
map_raw_write(map_priv, reg, val);
reg = MAP_TOP_CTRL_REG_1;
val = 0x1;
map_raw_write(map_priv, reg, val);
map_is_reset = true;
}
spin_unlock(&map_priv->map_lock);
if (map_is_reset) {
map_set_sleep_vol(map_priv, 0);
/* release constaint to allow LPM if needed */
if (map_priv->lpm_qos >= 0)
pm_qos_update_request(&map_priv->qos_idle,
PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
}
return;
}
EXPORT_SYMBOL(map_be_reset);
/* check if map is used by CP(i2s2) or FM(i2s3) */
static int map_i2s2_i2s3_active(struct map_private *map_priv)
{
unsigned int reg, val;
reg = MAP_I2S2_CTRL_REG;
val = map_raw_read(map_priv, reg);
if ((val & I2S_GEN_EN) || (val & I2S_REC_EN))
return 1;
reg = MAP_I2S3_CTRL_REG;
val = map_raw_read(map_priv, reg);
if ((val & I2S_GEN_EN) || (val & I2S_REC_EN))
return 1;
return 0;
}
/* used to check whether map is lite version */
bool map_get_lite_attr(void)
{
return audio_map_priv->map_lite;
}
EXPORT_SYMBOL(map_get_lite_attr);
static int device_map_init(struct map_private *map_priv)
{
int ret = 0;
struct regmap *regmap = map_priv->regmap;
if (!regmap) {
dev_err(map_priv->dev, "regmap is invalid\n");
return -EINVAL;
}
map_aux_clk_init(map_priv);
dev_info(map_priv->dev, "MAP config, please wait...\r\n");
map_config(map_priv);
map_internal_clk_init(map_priv);
dev_info(map_priv->dev, "DSP1 firmware version: 0x%x\r\n",
map_priv->dsp1_sw_id);
dev_info(map_priv->dev, "DSP2 firmware version: 0x%x\r\n",
map_priv->dsp2_sw_id);
dev_info(map_priv->dev, "DSP1A firmware version: 0x%x\r\n",
map_priv->dsp1a_sw_id);
ret = mfd_add_devices(map_priv->dev, 0, &sub_devs[0],
ARRAY_SIZE(sub_devs), 0, 0, NULL);
if (ret < 0) {
dev_err(map_priv->dev, "Failed to add sub_devs\n");
return ret;
}
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id mmp_map_of_match[] = {
{ .compatible = "marvell,mmp-map"},
{},
};
#endif
static int mmp_map_parse_dt(struct platform_device *pdev,
struct map_private *map_priv)
{
struct device_node *np = pdev->dev.of_node;
u32 pmu_audio_reg, bit_sram, bit_apb, pll_sel;
const char *pmic_name;
int sleep_vol;
int ret = 0;
if (!np) {
dev_err(&pdev->dev, "could not find marvell,mmp-map node\n");
return -ENODEV;
}
ret = of_property_read_u32(np, "audio_reg", &pmu_audio_reg);
if (ret < 0) {
dev_err(&pdev->dev, "could not get audio_reg in dt\n");
return ret;
}
map_priv->audio_reg = pmu_audio_reg;
ret = of_property_read_u32(np, "bit_sram", &bit_sram);
if (ret < 0) {
dev_err(&pdev->dev, "could not get bit_sram in dt\n");
return ret;
}
map_priv->bit_sram = bit_sram;
ret = of_property_read_u32(np, "bit_apb", &bit_apb);
if (ret < 0) {
dev_err(&pdev->dev, "could not get bit_apb in dt\n");
return ret;
}
map_priv->bit_apb = bit_apb;
ret = of_property_read_u32(np, "pll_sel", &pll_sel);
if (ret < 0) {
dev_err(&pdev->dev, "could not get pll_sel in dt\n");
return ret;
}
map_priv->pll_sel = pll_sel;
if (of_property_read_bool(np, "marvell,map-lite"))
map_priv->map_lite = true;
else
map_priv->map_lite = false;
if (of_property_read_bool(np, "marvell,b0_fix"))
map_priv->b0_fix = true;
else
map_priv->b0_fix = false;
ret = of_property_read_u32(np, "sleep_vol", &sleep_vol);
/* if sleep_vol is not specificed, do not set audio mode voltage */
if (ret >= 0) {
map_priv->sleep_vol = sleep_vol;
of_property_read_string(np, "pmic-name", &pmic_name);
if (!strcmp(pmic_name, "88pm880"))
pmic_is_88pm880 = true;
else
pmic_is_88pm880 = false;
pr_info("pmic_name = %s\n", pmic_name);
/* set audio mode voltage */
if (!pmic_is_88pm880) {
map_priv->vccmain = regulator_get(&pdev->dev, "vccmain");
if (IS_ERR(map_priv->vccmain)) {
map_priv->vccmain = NULL;
dev_err(&pdev->dev, "no vccmain set for audio mdoe\n");
} else {
regulator_set_suspend_voltage(map_priv->vccmain,
sleep_vol);
}
}
} else
map_priv->sleep_vol = 0;
return 0;
}
static int mmp_map_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct map_private *map_priv;
struct resource *res0, *res1, *region;
struct clk *clk;
struct clk_audio *clk_audio;
int ret = 0;
s32 lpm_qos;
map_priv = devm_kzalloc(&pdev->dev,
sizeof(struct map_private), GFP_KERNEL);
if (map_priv == NULL)
return -ENOMEM;
/* MAP AUX register area */
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res0 == NULL) {
dev_err(map_priv->dev, "resource for %s not exist\n",
pdev->name);
return -ENXIO;
}
/* map MAP register area */
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res1 == NULL) {
dev_err(map_priv->dev, "resource for %s not exist\n",
pdev->name);
return -ENXIO;
}
region = devm_request_mem_region(&pdev->dev, res0->start,
resource_size(res0), DRV_NAME);
if (!region) {
dev_err(&pdev->dev, "request region aux failed\n");
return -EBUSY;
}
map_priv->regs_aux = devm_ioremap(&pdev->dev, res0->start,
resource_size(res0));
if (!map_priv->regs_aux) {
dev_err(&pdev->dev, "ioremap aux failed\n");
return -ENOMEM;
}
region = devm_request_mem_region(&pdev->dev, res1->start,
resource_size(res1), DRV_NAME);
if (!region) {
dev_err(&pdev->dev, "request region map failed\n");
return -EBUSY;
}
map_priv->regs_map = devm_ioremap(&pdev->dev, res1->start,
resource_size(res1));
if (!map_priv->regs_map) {
dev_err(&pdev->dev, "ioremap map failed\n");
return -ENOMEM;
}
map_priv->dev = &pdev->dev;
map_priv->regmap = devm_regmap_init_mmio(&pdev->dev, map_priv->regs_map,
&mmp_map_regmap_config);
if (IS_ERR(map_priv->regmap)) {
ret = PTR_ERR(map_priv->regmap);
dev_err(map_priv->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
ret = of_property_read_u32(np, "marvell,apll", &map_priv->apll);
if (ret < 0) {
dev_err(&pdev->dev, "could not get marvell,apll in dt\n");
return ret;
}
ret = mmp_map_parse_dt(pdev, map_priv);
if (ret < 0) {
dev_err(&pdev->dev, "parse dt fail\n");
return ret;
}
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "get map_apll failed\n");
return PTR_ERR(clk);
}
clk_audio = to_clk_audio(clk->hw);
map_priv->apmu_base = clk_audio->apmu_base;
map_priv->apbsp_base = clk_audio->apbsp_base;
map_priv->regs_apmu = clk_audio->apmu_base + map_priv->audio_reg;
map_priv->poweron = clk_audio->poweron;
map_priv->user_count = 0;
map_priv->path_enabled = false;
map_priv->dsp1_mute = true;
map_priv->dsp2_mute = true;
map_priv->dsp1a_mute = true;
map_priv->map_i2s2_i2s3_active = false;
spin_lock_init(&map_priv->map_lock);
platform_set_drvdata(pdev, map_priv);
ret = device_map_init(map_priv);
if (ret) {
dev_err(map_priv->dev, "%s failed!\n", __func__);
return ret;
}
ret = of_property_read_u32(np, "lpm-qos", &lpm_qos);
if (ret == 0) {
map_priv->lpm_qos = lpm_qos;
map_priv->qos_idle.name = pdev->name;
pm_qos_add_request(&map_priv->qos_idle, PM_QOS_CPUIDLE_BLOCK,
PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
} else {
map_priv->lpm_qos = -1;
dev_dbg(&pdev->dev, "no lpm_qos required in dt\n");
}
/* add debug interface */
audio_map_priv = map_priv;
regmap_aux = map_priv->regs_aux;
/* add map_reg sysfs entries */
ret = device_create_file(&pdev->dev, &dev_attr_map_reg);
if (ret < 0) {
dev_err(&pdev->dev,
"%s: failed to add map_reg sysfs files: %d\n",
__func__, ret);
goto create_map_file_err;
}
/* add dspaux_reg sysfs entries */
ret = device_create_file(&pdev->dev, &dev_attr_dspaux_reg);
if (ret < 0) {
dev_err(&pdev->dev,
"%s: failed to add map_reg sysfs files: %d\n",
__func__, ret);
goto create_dspaux_file_err;
}
ret = create_update_firmware_file(pdev);
if (ret < 0)
goto create_firmware_err;
return 0;
create_firmware_err:
device_remove_file(&pdev->dev, &dev_attr_dspaux_reg);
create_dspaux_file_err:
device_remove_file(&pdev->dev, &dev_attr_map_reg);
create_map_file_err:
mfd_remove_devices(map_priv->dev);
pm_qos_remove_request(&map_priv->qos_idle);
return ret;
}
static int mmp_map_remove(struct platform_device *pdev)
{
struct map_private *map_priv;
device_remove_file(&pdev->dev, &dev_attr_map_reg);
device_remove_file(&pdev->dev, &dev_attr_dspaux_reg);
device_remove_file(&pdev->dev, &dev_attr_firmware_update);
map_priv = platform_get_drvdata(pdev);
if (map_priv->lpm_qos >= 0)
pm_qos_remove_request(&map_priv->qos_idle);
mfd_remove_devices(map_priv->dev);
platform_set_drvdata(pdev, NULL);
return 0;
}
/*
* On pxa1928, the register are cleared when entering D2
* So need to re-config them. But on pxa1U88, it use retaining mode
* so do nothing for entering/exiting D2
*/
static int map_suspend(struct device *dev)
{
struct map_private *map_priv = dev_get_drvdata(dev);
if (map_i2s2_i2s3_active(map_priv)) {
map_priv->map_i2s2_i2s3_active = true;
return 0;
}
map_priv->map_i2s2_i2s3_active = false;
map_save_aux_reg(map_priv);
if (map_priv->apll == APLL_32K)
map_32k_apll_disable(map_priv);
else
map_26m_apll_disable(map_priv);
return 0;
}
static int map_resume(struct device *dev)
{
struct map_private *map_priv = dev_get_drvdata(dev);
if (map_priv->map_i2s2_i2s3_active)
return 0;
map_priv->map_i2s2_i2s3_active = false;
if (map_priv->apll == APLL_32K)
map_32k_apll_enable(map_priv);
else
map_26m_apll_enable(map_priv);
map_restore_aux_reg(map_priv);
map_config(map_priv);
return 0;
}
static UNIVERSAL_DEV_PM_OPS(map_pm_ops, map_suspend, map_resume,
NULL);
static struct platform_driver mmp_map_driver = {
.probe = mmp_map_probe,
.remove = mmp_map_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &map_pm_ops,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(mmp_map_of_match),
#endif
},
};
module_platform_driver(mmp_map_driver);
MODULE_DESCRIPTION("Marvell MAP MFD driver");
MODULE_AUTHOR("Nenghua Cao <nhcao@marvell.com>");
MODULE_LICENSE("GPL");