blob: d7a16a0a30214621b0a207eff19c0909307e2c33 [file] [log] [blame]
/*
* sound/soc/amlogic/auge/ddr_mngr.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* 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.
*
* 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.
*
*/
#define DEBUG
#undef pr_fmt
#define pr_fmt(fmt) "audio_ddr_mngr: " fmt
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/notifier.h>
#include <linux/suspend.h>
#include "regs.h"
#include "ddr_mngr.h"
#include "audio_utils.h"
#include "resample.h"
#include "resample_hw.h"
#include "effects_hw.h"
#include "effects_hw_v2.h"
#include "effects_v2.h"
#include "pwrdet_hw.h"
#include "vad.h"
#define DRV_NAME "audio-ddr-manager"
static DEFINE_MUTEX(ddr_mutex);
#define DDRMAX 4
static struct frddr frddrs[DDRMAX];
static struct toddr toddrs[DDRMAX];
/* resample */
static struct toddr_attach attach_resample_a;
static struct toddr_attach attach_resample_b;
static void aml_check_resample(struct toddr *to, bool enable);
/* power detect */
static struct toddr_attach attach_pwrdet;
static void aml_check_pwrdet(bool enable);
static bool aml_check_pwrdet_module(int src);
/* VAD */
static struct toddr_attach attach_vad;
static void aml_check_vad(struct toddr *to, bool enable);
/* Audio EQ DRC */
static struct frddr_attach attach_aed;
static void aml_check_aed(bool enable, int dst);
static bool aml_check_aed_module(int dst);
/* to DDRS */
static struct toddr *register_toddr_l(struct device *dev,
struct aml_audio_controller *actrl,
irq_handler_t handler, void *data)
{
struct toddr *to;
unsigned int mask_bit;
int i, ret;
/* lookup unused toddr */
for (i = 0; i < DDRMAX; i++) {
if (!toddrs[i].in_use)
break;
}
if (i >= DDRMAX)
return NULL;
to = &toddrs[i];
/* irqs request */
ret = request_irq(to->irq, handler,
0, dev_name(dev), data);
if (ret) {
dev_err(dev, "failed to claim irq %u\n", to->irq);
return NULL;
}
/* enable audio ddr arb */
mask_bit = i;
/*aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,*/
/* (1 << 31)|(1 << mask_bit),*/
/* (1 << 31)|(1 << mask_bit));*/
to->dev = dev;
to->actrl = actrl;
to->in_use = true;
pr_debug("toddrs[%d] registered by device %s\n", i, dev_name(dev));
return to;
}
static int unregister_toddr_l(struct device *dev, void *data)
{
struct toddr *to;
struct aml_audio_controller *actrl;
unsigned int mask_bit;
unsigned int value;
int i;
if (dev == NULL)
return -EINVAL;
for (i = 0; i < DDRMAX; i++) {
if ((toddrs[i].dev) == dev && toddrs[i].in_use)
break;
}
if (i >= DDRMAX)
return -EINVAL;
to = &toddrs[i];
/* check for loopback */
if (to->is_lb) {
loopback_set_status(0);
to->is_lb = 0;
}
/* disable audio ddr arb */
mask_bit = i;
actrl = to->actrl;
/*aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,*/
/* 1 << mask_bit, 0 << mask_bit);*/
/* no ddr active, disable arb switch */
value = aml_audiobus_read(actrl, EE_AUDIO_ARB_CTRL) & 0x77;
/*if (value == 0)*/
/* aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,*/
/* 1 << 31, 0 << 31);*/
free_irq(to->irq, data);
to->dev = NULL;
to->actrl = NULL;
to->in_use = false;
pr_debug("toddrs[%d] released by device %s\n", i, dev_name(dev));
return 0;
}
int fetch_toddr_index_by_src(int toddr_src)
{
int i;
for (i = 0; i < DDRMAX; i++) {
if (toddrs[i].in_use
&& (toddrs[i].src == toddr_src)) {
return i;
}
}
return -1;
}
struct toddr *fetch_toddr_by_src(int toddr_src)
{
int i;
for (i = 0; i < DDRMAX; i++) {
if (toddrs[i].in_use
&& (toddrs[i].src == toddr_src)) {
return &toddrs[i];
}
}
return NULL;
}
struct toddr *aml_audio_register_toddr(struct device *dev,
struct aml_audio_controller *actrl,
irq_handler_t handler, void *data)
{
struct toddr *to = NULL;
mutex_lock(&ddr_mutex);
to = register_toddr_l(dev, actrl,
handler, data);
mutex_unlock(&ddr_mutex);
return to;
}
int aml_audio_unregister_toddr(struct device *dev, void *data)
{
int ret;
mutex_lock(&ddr_mutex);
ret = unregister_toddr_l(dev, data);
mutex_unlock(&ddr_mutex);
return ret;
}
void audio_toddr_irq_enable(struct toddr *to, bool en)
{
if (!to || !to->in_use || to->irq < 0)
return;
mutex_lock(&ddr_mutex);
if (en)
enable_irq(to->irq);
else
disable_irq_nosync(to->irq);
mutex_unlock(&ddr_mutex);
}
static inline unsigned int
calc_toddr_address(unsigned int reg, unsigned int base)
{
return base + reg - EE_AUDIO_TODDR_A_CTRL0;
}
int aml_toddr_set_buf(struct toddr *to, unsigned int start,
unsigned int end)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
to->start_addr = start;
to->end_addr = end;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_START_ADDR, reg_base);
aml_audiobus_write(actrl, reg, start);
reg = calc_toddr_address(EE_AUDIO_TODDR_A_FINISH_ADDR, reg_base);
aml_audiobus_write(actrl, reg, end);
/* int address */
if (to->chipinfo
&& (!to->chipinfo->int_start_same_addr)) {
reg = calc_toddr_address(EE_AUDIO_TODDR_A_INIT_ADDR, reg_base);
aml_audiobus_write(actrl, reg, start);
}
return 0;
}
int aml_toddr_set_buf_startaddr(struct toddr *to, unsigned int start)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
to->start_addr = start;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_START_ADDR, reg_base);
aml_audiobus_write(actrl, reg, start);
/* int address */
if (to->chipinfo
&& (!to->chipinfo->int_start_same_addr)) {
reg = calc_toddr_address(EE_AUDIO_TODDR_A_INIT_ADDR, reg_base);
aml_audiobus_write(actrl, reg, start);
}
return 0;
}
int aml_toddr_set_buf_endaddr(struct toddr *to, unsigned int end)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
to->end_addr = end;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_FINISH_ADDR, reg_base);
aml_audiobus_write(actrl, reg, end);
return 0;
}
int aml_toddr_set_intrpt(struct toddr *to, unsigned int intrpt)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_INT_ADDR, reg_base);
aml_audiobus_write(actrl, reg, intrpt);
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 0xff << 16, 0x34 << 16);
return 0;
}
unsigned int aml_toddr_get_position(struct toddr *to)
{
return aml_toddr_read_status2(to);
}
unsigned int aml_toddr_get_addr(struct toddr *to, enum status_sel sel)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg_sel, reg, addr;
reg_sel = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
aml_audiobus_update_bits(actrl, reg_sel,
0xf << 8,
sel << 8);
reg = calc_toddr_address(EE_AUDIO_TODDR_A_STATUS2, reg_base);
addr = aml_audiobus_read(actrl, reg);
if (sel == VAD_WAKEUP_ADDR) {
/* clear VAD addr/cnt */
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 0x1 << 1, 0x1 << 1);
}
/* reset to default, current write addr */
aml_audiobus_update_bits(actrl, reg_sel,
0xf << 8,
0x2 << 8);
return addr;
}
void aml_toddr_enable(struct toddr *to, bool enable)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 1<<31, enable<<31);
/* check resample */
aml_check_resample(to, enable);
if (to->chipinfo
&& to->chipinfo->wakeup) {
if (to->chipinfo->wakeup == 1) {
/* check power detect */
if (aml_check_pwrdet_module(to->src))
aml_check_pwrdet(enable);
} else if (to->chipinfo->wakeup == 2)
/* check VAD */
aml_check_vad(to, enable);
}
if (!enable)
aml_audiobus_write(actrl, reg, 0x0);
}
void aml_toddr_select_src(struct toddr *to, enum toddr_src src)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
/* store to check toddr num */
to->src = src;
/* check whether loopback enable */
if (loopback_check_enable(src)) {
loopback_set_status(1);
to->is_lb = 1; /* in loopback */
src = LOOPBACK_A;
}
if (to->chipinfo
&& to->chipinfo->src_sel_ctrl) {
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
aml_audiobus_update_bits(actrl, reg,
0xf << 28,
(src & 0xf) << 28);
} else {
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 0x7, src & 0x7);
}
}
void aml_toddr_set_fifos(struct toddr *to, unsigned int thresh)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg, mask, val;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
if (to->chipinfo
&& to->chipinfo->src_sel_ctrl) {
mask = 0xfff << 12 | 0xf << 8;
val = (thresh-2) << 12 | 2 << 8;
} else {
mask = 0xff << 16 | 0xf << 8;
val = (thresh-2) << 16 | 2 << 8;
}
aml_audiobus_update_bits(actrl, reg, mask, val);
if (to->chipinfo && to->chipinfo->ugt) {
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 0x0 << 0, 0x1 << 0);
}
}
void aml_toddr_update_fifos_rd_th(struct toddr *to, int th)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg, mask, val;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
if (to->chipinfo
&& to->chipinfo->src_sel_ctrl) {
mask = 0xfff << 12;
val = (th - 1) << 12;
} else {
mask = 0xff << 16;
val = (th - 1) << 16;
}
aml_audiobus_update_bits(actrl, reg, mask, val);
}
void aml_toddr_force_finish(struct toddr *to)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
aml_audiobus_update_bits(actrl, reg, 1 << 25, 1 << 25);
aml_audiobus_update_bits(actrl, reg, 1 << 25, 0 << 25);
}
void aml_toddr_set_format(struct toddr *to, struct toddr_fmt *fmt)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
to->bitdepth = fmt->bit_depth;
to->channels = fmt->ch_num;
to->rate = fmt->rate;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg,
0x7 << 24 | 0x1fff << 3,
fmt->endian << 24 | fmt->type << 13 |
fmt->msb << 8 | fmt->lsb << 3);
}
void aml_toddr_insert_chanum(struct toddr *to)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
aml_audiobus_update_bits(actrl, reg, 1 << 24, 1 << 24);
}
unsigned int aml_toddr_read(struct toddr *to)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
return aml_audiobus_read(actrl, reg);
}
void aml_toddr_write(struct toddr *to, unsigned int val)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_write(actrl, reg, val);
}
unsigned int aml_toddr_read1(struct toddr *to)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
return aml_audiobus_read(actrl, reg);
}
void aml_toddr_write1(struct toddr *to, unsigned int val)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
aml_audiobus_write(actrl, reg, val);
}
unsigned int aml_toddr_read_status2(struct toddr *to)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_STATUS2, reg_base);
return aml_audiobus_read(actrl, reg);
}
bool aml_toddr_burst_finished(struct toddr *to)
{
unsigned int addr_request, addr_reply, i = 0;
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
/* max 200us delay */
for (i = 0; i < 200; i++) {
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
aml_audiobus_update_bits(actrl, reg, 0xf << 8, 0x0 << 8);
addr_request = aml_toddr_get_position(to);
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
aml_audiobus_update_bits(actrl, reg, 0xf << 8, 0x2 << 8);
addr_reply = aml_toddr_get_position(to);
if (addr_request == addr_reply)
return true;
udelay(1);
pr_debug("delay:[%dus]; FRDDR_STATUS2: [0x%x] [0x%x]\n",
i, addr_request, addr_reply);
}
pr_err("Error: 200us time out, TODDR_STATUS2: [0x%x] [0x%x]\n",
addr_request, addr_reply);
return false;
}
/* not for tl1 */
static void aml_toddr_set_resample(struct toddr *to, bool enable)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 1<<30, !!enable<<30);
}
/* tl1 after */
static void aml_toddr_set_resample_ab(struct toddr *to,
enum resample_idx index, bool enable)
{
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL1, reg_base);
if (index == RESAMPLE_A)
aml_audiobus_update_bits(actrl, reg, 1 << 27, !!enable << 27);
else if (index == RESAMPLE_B)
aml_audiobus_update_bits(actrl, reg, 1 << 26, !!enable << 26);
}
static void aml_resample_enable(
struct toddr *to,
struct toddr_attach *p_attach_resample,
bool enable)
{
if (!to || !p_attach_resample) {
pr_err("%s(), NULL pointer.", __func__);
return;
}
if (to->chipinfo
&& to->chipinfo->asrc_src_sel_ctrl) {
/* fix asrc_src_sel */
/*
switch (p_attach_resample->attach_module) {
case LOOPBACK_A:
to->asrc_src_sel = ASRC_LOOPBACK_A;
break;
case LOOPBACK_B:
to->asrc_src_sel = ASRC_LOOPBACK_B;
break;
default:
to->asrc_src_sel = to->fifo_id;
break;
}
*/
to->asrc_src_sel = p_attach_resample->attach_module;
}
pr_info("toddr %d selects data to %s resample_%c for module:%s\n",
to->fifo_id,
enable ? "enable" : "disable",
(p_attach_resample->id == RESAMPLE_A) ? 'a' : 'b',
toddr_src_get_str(p_attach_resample->attach_module)
);
if (enable) {
int bitwidth = to->bitdepth;
/* channels and bit depth for resample */
if (to->chipinfo
&& to->chipinfo->asrc_only_left_j
/*&& (to->src == SPDIFIN)*/
&& (bitwidth == 32)) {
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg;
unsigned int endian, toddr_type;
/* TODO: fixed me */
pr_info("Warning: Not support 32bit sample rate for axg chipset\n");
bitwidth = 24;
endian = 5;
toddr_type = 4;
/* FIX ME */
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0,
reg_base);
aml_audiobus_update_bits(actrl, reg,
0x7 << 24 | 0x7 << 13,
endian << 24 | toddr_type << 13);
}
resample_format_set(p_attach_resample->id,
to->channels, bitwidth);
/* toddr index for resample */
if (to->chipinfo
&& to->chipinfo->asrc_src_sel_ctrl)
resample_src_select_ab(p_attach_resample->id,
to->asrc_src_sel);
else
resample_src_select(to->fifo_id);
}
/* resample enable or not */
resample_enable(p_attach_resample->id, enable);
/* select reample data */
if (to->chipinfo
&& to->chipinfo->asrc_src_sel_ctrl)
aml_toddr_set_resample_ab(to, p_attach_resample->id, enable);
else
aml_toddr_set_resample(to, enable);
}
void aml_set_resample(enum resample_idx id,
bool enable, enum toddr_src resample_module)
{
struct toddr_attach *p_attach_resample;
struct toddr *to;
bool update_running = false;
if (id == RESAMPLE_A)
p_attach_resample = &attach_resample_a;
else
p_attach_resample = &attach_resample_b;
p_attach_resample->enable = enable;
p_attach_resample->id = id;
p_attach_resample->attach_module = resample_module;
to = fetch_toddr_by_src(
p_attach_resample->attach_module);
if (enable) {
if ((p_attach_resample->status == DISABLED)
|| (p_attach_resample->status == READY)) {
if (!to) {
p_attach_resample->status = READY;
} else {
p_attach_resample->status = RUNNING;
update_running = true;
pr_info("Capture with resample\n");
}
}
} else {
if (p_attach_resample->status == RUNNING)
update_running = true;
p_attach_resample->status = DISABLED;
}
if (update_running && to)
aml_resample_enable(to, p_attach_resample, enable);
}
/*
* when try to enable resample, if toddr is not in used,
* set resample status as ready
*/
static void aml_check_resample(struct toddr *to, bool enable)
{
struct toddr_attach *p_attach_resample;
bool is_module_resample;
bool resample_b_check = false;
p_attach_resample = &attach_resample_a;
start_check:
is_module_resample = false;
if (p_attach_resample->enable
&& (to->src == p_attach_resample->attach_module))
is_module_resample = true;
/* resample in enable */
if (is_module_resample) {
if (enable)
p_attach_resample->status = RUNNING;
else
p_attach_resample->status = DISABLED;
aml_resample_enable(to, p_attach_resample, enable);
}
if ((!resample_b_check)
&& (get_resample_module_num() == 2)) {
p_attach_resample = &attach_resample_b;
resample_b_check = true;
goto start_check;
}
}
static void aml_set_pwrdet(struct toddr *to,
bool enable)
{
if (enable) {
struct aml_audio_controller *actrl = to->actrl;
unsigned int reg_base = to->reg_base;
unsigned int reg, val;
unsigned int toddr_type, msb, lsb;
reg = calc_toddr_address(EE_AUDIO_TODDR_A_CTRL0, reg_base);
val = aml_audiobus_read(actrl, reg);
toddr_type = (val >> 13) & 0x7;
msb = (val >> 8) & 0x1f;
lsb = (val >> 3) & 0x1f;
aml_pwrdet_format_set(toddr_type, msb, lsb);
}
pwrdet_src_select(enable, to->src);
}
void aml_pwrdet_enable(bool enable, int pwrdet_module)
{
attach_pwrdet.enable = enable;
attach_pwrdet.attach_module = pwrdet_module;
if (enable) {
if ((attach_pwrdet.status == DISABLED)
|| (attach_pwrdet.status == READY)) {
struct toddr *to = fetch_toddr_by_src(pwrdet_module);
if (!to) {
attach_pwrdet.status = READY;
} else {
attach_pwrdet.status = RUNNING;
aml_set_pwrdet(to, enable);
pr_info("Capture with power detect\n");
}
}
} else {
if (attach_pwrdet.status == RUNNING) {
struct toddr *to = fetch_toddr_by_src(pwrdet_module);
if (to)
aml_set_pwrdet(to, enable);
}
attach_pwrdet.status = DISABLED;
}
}
static bool aml_check_pwrdet_module(int src)
{
bool is_module_pwrdet = false;
if (attach_pwrdet.enable
&& (src == attach_pwrdet.attach_module))
is_module_pwrdet = true;
return is_module_pwrdet;
}
static void aml_check_pwrdet(bool enable)
{
/* power detect in enable */
if (attach_pwrdet.enable) {
if (enable) {
/* check whether ready ? */
if (attach_pwrdet.status == READY)
aml_pwrdet_enable(true,
attach_pwrdet.attach_module);
} else {
if (attach_pwrdet.status == RUNNING)
attach_pwrdet.status = READY;
}
}
}
static void aml_vad_enable(
struct toddr_attach *p_attach_vad,
bool enable)
{
struct toddr *to = fetch_toddr_by_src(p_attach_vad->attach_module);
if (!to)
return;
vad_set_toddr_info(enable ? to : NULL);
/* vad enable or not */
vad_enable(enable);
}
void aml_set_vad(bool enable, int module)
{
struct toddr_attach *p_attach_vad = &attach_vad;
bool update_running = false;
p_attach_vad->enable = enable;
p_attach_vad->attach_module = module;
if (enable) {
if ((p_attach_vad->status == DISABLED)
|| (p_attach_vad->status == READY)) {
struct toddr *to = fetch_toddr_by_src(
p_attach_vad->attach_module);
if (!to) {
p_attach_vad->status = READY;
} else {
p_attach_vad->status = RUNNING;
update_running = true;
pr_info("Capture with VAD\n");
}
}
} else {
if (p_attach_vad->status == RUNNING)
update_running = true;
p_attach_vad->status = DISABLED;
}
if (update_running)
aml_vad_enable(p_attach_vad, enable);
}
/*
* when try to enable vad, if toddr is not in used,
* set vad status as ready
*/
static void aml_check_vad(struct toddr *to, bool enable)
{
struct toddr_attach *p_attach_vad = &attach_vad;
bool is_vad = false;
if (p_attach_vad->enable
&& (to->src == p_attach_vad->attach_module))
is_vad = true;
/* vad in enable */
if (is_vad) {
if (enable)
p_attach_vad->status = RUNNING;
else
p_attach_vad->status = DISABLED;
aml_vad_enable(p_attach_vad, enable);
}
}
/* from DDRS */
static struct frddr *register_frddr_l(struct device *dev,
struct aml_audio_controller *actrl,
irq_handler_t handler, void *data)
{
struct frddr *from;
unsigned int mask_bit;
int i, ret;
/* lookup unused frddr */
for (i = 0; i < DDRMAX; i++) {
if (!frddrs[i].in_use)
break;
}
if (i >= DDRMAX)
return NULL;
from = &frddrs[i];
/* enable audio ddr arb */
mask_bit = i + 4;
/*aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,*/
/* (1 << 31)|(1 << mask_bit),*/
/* (1 << 31)|(1 << mask_bit));*/
/* irqs request */
ret = request_irq(from->irq, handler,
0, dev_name(dev), data);
if (ret) {
dev_err(dev, "failed to claim irq %u\n", from->irq);
return NULL;
}
from->dev = dev;
from->actrl = actrl;
from->in_use = true;
pr_debug("frddrs[%d] registered by device %s\n", i, dev_name(dev));
return from;
}
static int unregister_frddr_l(struct device *dev, void *data)
{
struct frddr *from;
struct aml_audio_controller *actrl;
unsigned int mask_bit;
unsigned int value;
int i;
if (dev == NULL)
return -EINVAL;
for (i = 0; i < DDRMAX; i++) {
if ((frddrs[i].dev) == dev && frddrs[i].in_use)
break;
}
if (i >= DDRMAX)
return -EINVAL;
from = &frddrs[i];
/* disable audio ddr arb */
mask_bit = i + 4;
actrl = from->actrl;
/*aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,*/
/* 1 << mask_bit, 0 << mask_bit);*/
/* no ddr active, disable arb switch */
value = aml_audiobus_read(actrl, EE_AUDIO_ARB_CTRL) & 0x77;
/*if (value == 0)*/
/* aml_audiobus_update_bits(actrl, EE_AUDIO_ARB_CTRL,*/
/* 1 << 31, 0 << 31);*/
free_irq(from->irq, data);
from->dev = NULL;
from->actrl = NULL;
from->in_use = false;
pr_debug("frddrs[%d] released by device %s\n", i, dev_name(dev));
return 0;
}
int fetch_frddr_index_by_src(int frddr_src)
{
int i;
for (i = 0; i < DDRMAX; i++) {
if (frddrs[i].in_use
&& (frddrs[i].dest == frddr_src)) {
return i;
}
}
return -1;
}
struct frddr *fetch_frddr_by_src(int frddr_src)
{
int i;
for (i = 0; i < DDRMAX; i++) {
if (frddrs[i].in_use
&& (frddrs[i].dest == frddr_src)) {
return &frddrs[i];
}
}
return NULL;
}
struct frddr *aml_audio_register_frddr(struct device *dev,
struct aml_audio_controller *actrl,
irq_handler_t handler, void *data)
{
struct frddr *fr = NULL;
mutex_lock(&ddr_mutex);
fr = register_frddr_l(dev, actrl, handler, data);
mutex_unlock(&ddr_mutex);
return fr;
}
int aml_audio_unregister_frddr(struct device *dev, void *data)
{
int ret;
mutex_lock(&ddr_mutex);
ret = unregister_frddr_l(dev, data);
mutex_unlock(&ddr_mutex);
return ret;
}
static inline unsigned int
calc_frddr_address(unsigned int reg, unsigned int base)
{
return base + reg - EE_AUDIO_FRDDR_A_CTRL0;
}
/*
* check frddr_src is used by other frddr for sharebuffer
* if used, disabled the other share frddr src, the module would
* for current frddr, and the checked frddr
*/
int aml_check_sharebuffer_valid(struct frddr *fr, int ss_sel)
{
int current_fifo_id = fr->fifo_id;
unsigned int i;
int ret = 1;
for (i = 0; i < DDRMAX; i++) {
if (frddrs[i].in_use
&& (frddrs[i].fifo_id != current_fifo_id)
&& (frddrs[i].dest == ss_sel)) {
pr_debug("%s, ss_sel:%d used, not for share buffer at same time\n",
__func__,
ss_sel);
ret = 0;
break;
}
}
return ret;
}
/* select dst for same source
* sel: share buffer req_sel 1~2
* sel 0 is aleardy used for reg_frddr_src_sel1
* sel 1 is for reg_frddr_src_sel2
* sel 2 is for reg_frddr_src_sel3
*/
static void frddr_set_sharebuffer_enable(
struct frddr *fr, int dst, int sel, bool enable)
{
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
int s_v = 0, s_m = 0;
if (fr->chipinfo
&& fr->chipinfo->src_sel_ctrl) {
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2,
reg_base);
switch (sel) {
case 1:
s_m = 0x17 << 8;
s_v = enable ?
(dst << 8 | 1 << 12) : 0 << 8;
break;
case 2:
s_m = 0x17 << 16;
s_v = enable ?
(dst << 16 | 1 << 20) : 0 << 16;
break;
default:
pr_warn_once("sel :%d is not supported for same source\n",
sel);
break;
}
s_m |= 0xff << 24;
if (enable)
s_v |= (fr->channels - 1) << 24;
else
s_v |= 0x0 << 24;
} else {
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0,
reg_base);
switch (sel) {
case 1:
s_m = 0xf << 4;
s_v = enable ?
(dst << 4 | 1 << 7) : 0 << 4;
break;
case 2:
s_m = 0xf << 8;
s_v = enable ?
(dst << 8 | 1 << 11) : 0 << 8;
break;
default:
pr_warn_once("sel :%d is not supported for same source\n",
sel);
break;
}
}
pr_debug("%s sel:%d, dst_src:%d\n",
__func__, sel, dst);
fr->ss_dest = enable ? dst : 0;
fr->ss_en = enable;
aml_audiobus_update_bits(actrl, reg, s_m, s_v);
}
/*
* check frddr_src is used by other frddr for sharebuffer
* if used for share frddr src, release from sharebuffer
* and used for new frddr
*/
static int aml_check_and_release_sharebuffer(struct frddr *fr, int ss_sel)
{
int current_fifo_id = fr->fifo_id;
unsigned int i;
int ret = 1;
for (i = 0; i < DDRMAX; i++) {
struct frddr *from = &frddrs[i];
if (from->in_use
&& (from->fifo_id != current_fifo_id)
&& from->ss_en
&& (from->ss_dest == ss_sel)) {
frddr_set_sharebuffer_enable(from,
ss_sel,
1,
false);
pr_debug("%s, ss_sel:%d release from share buffer, use for new playback\n",
__func__,
ss_sel);
ret = 0;
break;
}
}
return ret;
}
int aml_frddr_set_buf(struct frddr *fr, unsigned int start,
unsigned int end)
{
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_START_ADDR, reg_base);
aml_audiobus_write(actrl, reg, start);
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_FINISH_ADDR, reg_base);
aml_audiobus_write(actrl, reg, end);
/* int address */
if (fr->chipinfo
&& (!fr->chipinfo->int_start_same_addr)) {
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_INIT_ADDR, reg_base);
aml_audiobus_write(actrl, reg, start);
}
return 0;
}
int aml_frddr_set_intrpt(struct frddr *fr, unsigned int intrpt)
{
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_INT_ADDR, reg_base);
aml_audiobus_write(actrl, reg, intrpt);
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 0xff<<16, 4<<16);
return 0;
}
unsigned int aml_frddr_get_position(struct frddr *fr)
{
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_STATUS2, reg_base);
return aml_audiobus_read(actrl, reg);
}
void aml_frddr_enable(struct frddr *fr, bool enable)
{
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base);
/* ensure disable before enable frddr */
aml_audiobus_update_bits(actrl, reg, 1<<31, enable<<31);
if (!enable) {
aml_audiobus_write(actrl, reg, 0x0);
/* clr src sel and its en */
if (fr->chipinfo
&& fr->chipinfo->src_sel_ctrl) {
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2,
reg_base);
aml_audiobus_write(actrl, reg, 0x0);
}
}
/* check for Audio EQ/DRC */
if (aml_check_aed_module(fr->dest))
aml_check_aed(enable, fr->dest);
}
void aml_frddr_select_dst(struct frddr *fr, enum frddr_dest dst)
{
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg, src_sel_en;
fr->dest = dst;
if (fr->chipinfo
&& fr->chipinfo->src_sel_ctrl) {
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2, reg_base);
src_sel_en = 4;
} else {
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base);
src_sel_en = 3;
}
/* if sharebuffer in use, release it */
if (fr->chipinfo
&& fr->chipinfo->same_src_fn)
aml_check_and_release_sharebuffer(fr, dst);
aml_audiobus_update_bits(actrl, reg, 0x7, dst & 0x7);
/* same source en */
if (fr->chipinfo
&& fr->chipinfo->same_src_fn) {
aml_audiobus_update_bits(actrl, reg,
1 << src_sel_en, 1 << src_sel_en);
}
}
/* select dst for same source
* sel: share buffer req_sel 1~2
* sel 0 is aleardy used for reg_frddr_src_sel1
* sel 1 is for reg_frddr_src_sel2
* sel 2 is for reg_frddr_src_sel3
*/
void aml_frddr_select_dst_ss(struct frddr *fr,
enum frddr_dest dst, int sel, bool enable)
{
unsigned int ss_valid = aml_check_sharebuffer_valid(fr, dst);
/* same source en */
if (fr->chipinfo
&& fr->chipinfo->same_src_fn
&& ss_valid
)
frddr_set_sharebuffer_enable(fr, dst, sel, enable);
}
void aml_frddr_set_fifos(struct frddr *fr,
unsigned int depth, unsigned int thresh)
{
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL1, reg_base);
aml_audiobus_update_bits(actrl, reg,
0xffff<<16 | 0xf<<8,
(depth - 1)<<24 | (thresh - 1)<<16 | 2<<8);
if (fr->chipinfo && fr->chipinfo->ugt) {
reg = calc_toddr_address(EE_AUDIO_FRDDR_A_CTRL0, reg_base);
aml_audiobus_update_bits(actrl, reg, 0x0 << 0, 0x1 << 0);
}
}
unsigned int aml_frddr_get_fifo_id(struct frddr *fr)
{
return fr->fifo_id;
}
void aml_frddr_set_format(struct frddr *fr,
unsigned int chnum,
unsigned int msb,
unsigned int frddr_type)
{
fr->channels = chnum;
fr->msb = msb;
fr->type = frddr_type;
}
static void aml_aed_enable(struct frddr_attach *p_attach_aed, bool enable)
{
struct frddr *fr = fetch_frddr_by_src(p_attach_aed->attach_module);
if (check_aed_v2()) {
struct aml_audio_controller *actrl = fr->actrl;
unsigned int reg_base = fr->reg_base;
unsigned int reg;
reg = calc_frddr_address(EE_AUDIO_FRDDR_A_CTRL2, reg_base);
aml_audiobus_update_bits(actrl,
reg, 0x1 << 3, enable << 3);
aed_set_ctrl(enable, 0, p_attach_aed->attach_module);
aed_set_format(fr->msb, fr->type, fr->fifo_id);
aed_enable(enable);
} else {
if (enable) {
/* frddr type and bit depth for AED */
aml_aed_format_set(fr->dest);
}
aed_src_select(enable, fr->dest, fr->fifo_id);
}
}
void aml_set_aed(bool enable, int aed_module)
{
bool update_running = false;
/* when try to enable AED, if frddr is not in used,
* set AED status as ready
*/
attach_aed.enable = enable;
attach_aed.attach_module = aed_module;
if (enable) {
if ((attach_aed.status == DISABLED)
|| (attach_aed.status == READY)) {
struct frddr *fr = fetch_frddr_by_src(aed_module);
if (!fr) {
attach_aed.status = READY;
} else {
attach_aed.status = RUNNING;
update_running = true;
pr_info("Playback with AED\n");
}
}
} else {
if (attach_aed.status == RUNNING)
update_running = true;
attach_aed.status = DISABLED;
}
if (update_running)
aml_aed_enable(&attach_aed, enable);
}
static bool aml_check_aed_module(int dst)
{
bool is_module_aed = false;
if (attach_aed.enable
&& (dst == attach_aed.attach_module))
is_module_aed = true;
return is_module_aed;
}
static void aml_check_aed(bool enable, int dst)
{
/* check effect module is sync with crruent frddr dst */
if (attach_aed.attach_module != dst)
return;
/* AED in enable */
if (attach_aed.enable) {
if (enable)
attach_aed.status = RUNNING;
else
attach_aed.status = DISABLED;
aml_aed_enable(&attach_aed, enable);
}
}
void frddr_init_without_mngr(unsigned int frddr_index, unsigned int src0_sel)
{
unsigned int offset, reg;
unsigned int start_addr, end_addr, int_addr;
static int buf[256];
memset(buf, 0x0, sizeof(buf));
start_addr = virt_to_phys(buf);
end_addr = start_addr + sizeof(buf) - 1;
int_addr = sizeof(buf) / 64;
offset = EE_AUDIO_FRDDR_B_START_ADDR - EE_AUDIO_FRDDR_A_START_ADDR;
reg = EE_AUDIO_FRDDR_A_START_ADDR + offset * frddr_index;
audiobus_write(reg, start_addr);
offset = EE_AUDIO_FRDDR_B_INIT_ADDR - EE_AUDIO_FRDDR_A_INIT_ADDR;
reg = EE_AUDIO_FRDDR_A_INIT_ADDR + offset * frddr_index;
audiobus_write(reg, start_addr);
offset = EE_AUDIO_FRDDR_B_FINISH_ADDR - EE_AUDIO_FRDDR_A_FINISH_ADDR;
reg = EE_AUDIO_FRDDR_A_FINISH_ADDR + offset * frddr_index;
audiobus_write(reg, end_addr);
offset = EE_AUDIO_FRDDR_B_INT_ADDR - EE_AUDIO_FRDDR_A_INT_ADDR;
reg = EE_AUDIO_FRDDR_A_INT_ADDR + offset * frddr_index;
audiobus_write(reg, int_addr);
offset = EE_AUDIO_FRDDR_B_CTRL1 - EE_AUDIO_FRDDR_A_CTRL1;
reg = EE_AUDIO_FRDDR_A_CTRL1 + offset * frddr_index;
audiobus_write(reg,
(0x40 - 1) << 24 | (0x20 - 1) << 16 | 2 << 8 | 0 << 0);
offset = EE_AUDIO_FRDDR_B_CTRL0 - EE_AUDIO_FRDDR_A_CTRL0;
reg = EE_AUDIO_FRDDR_A_CTRL0 + offset * frddr_index;
audiobus_write(reg,
1 << 31
| 0 << 24
| 4 << 16
| 1 << 3 /* src0 enable */
| src0_sel << 0 /* src0 sel */
);
}
void frddr_deinit_without_mngr(unsigned int frddr_index)
{
unsigned int offset, reg;
offset = EE_AUDIO_FRDDR_B_CTRL0 - EE_AUDIO_FRDDR_A_CTRL0;
reg = EE_AUDIO_FRDDR_A_CTRL0 + offset * frddr_index;
audiobus_write(reg, 0x0);
}
static enum toddr_src toddr_src_idx = TODDR_INVAL;
static const char *const toddr_src_sel_texts[] = {
"TDMIN_A", "TDMIN_B", "TDMIN_C", "SPDIFIN",
"PDMIN", "FRATV", "TDMIN_LB", "LOOPBACK_A",
"FRHDMIRX", "LOOPBACK_B", "SPDIFIN_LB",
"EARCRX_DMAC", "RESERVED_0", "RESERVED_1", "RESERVED_2",
"VAD"
};
static const struct soc_enum toddr_input_source_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(toddr_src_sel_texts),
toddr_src_sel_texts);
enum toddr_src toddr_src_get(void)
{
return toddr_src_idx;
}
const char *toddr_src_get_str(enum toddr_src idx)
{
if (idx < TDMIN_A || idx > VAD)
return NULL;
return toddr_src_sel_texts[idx];
}
static int toddr_src_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.enumerated.item[0] = toddr_src_idx;
return 0;
}
static int toddr_src_enum_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
toddr_src_idx = ucontrol->value.enumerated.item[0];
/* also update to resample src */
//set_resample_source(toddr_src_idx);
return 0;
}
static int frddr_src_idx = -1;
static const char *const frddr_src_sel_texts[] = {
"TDMOUT_A", "TDMOUT_B", "TDMOUT_C", "SPDIFOUT_A", "SPDIFOUT_B"
};
static const struct soc_enum frddr_output_source_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(frddr_src_sel_texts),
frddr_src_sel_texts);
int frddr_src_get(void)
{
return frddr_src_idx;
}
const char *frddr_src_get_str(int idx)
{
if (idx < 0 || idx > 4)
return NULL;
return frddr_src_sel_texts[idx];
}
static int frddr_src_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.enumerated.item[0] = frddr_src_idx;
return 0;
}
static int frddr_src_enum_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
frddr_src_idx = ucontrol->value.enumerated.item[0];
return 0;
}
static const struct snd_kcontrol_new snd_ddr_controls[] = {
SOC_ENUM_EXT("Audio In Source",
toddr_input_source_enum,
toddr_src_enum_get,
toddr_src_enum_set),
SOC_ENUM_EXT("Audio Out Sink",
toddr_input_source_enum,
frddr_src_enum_get,
frddr_src_enum_set),
};
int card_add_ddr_kcontrols(struct snd_soc_card *card)
{
unsigned int idx;
int err;
for (idx = 0; idx < ARRAY_SIZE(snd_ddr_controls); idx++) {
err = snd_ctl_add(card->snd_card,
snd_ctl_new1(&snd_ddr_controls[idx],
NULL));
if (err < 0)
return err;
}
return 0;
}
static struct ddr_chipinfo axg_ddr_chipinfo = {
.int_start_same_addr = true,
.asrc_only_left_j = true,
.wakeup = 1,
};
static struct ddr_chipinfo g12a_ddr_chipinfo = {
.same_src_fn = true,
.asrc_only_left_j = true,
.wakeup = 1,
};
static struct ddr_chipinfo tl1_ddr_chipinfo = {
.same_src_fn = true,
.ugt = true,
.src_sel_ctrl = true,
.asrc_src_sel_ctrl = true,
.fifo_num = 4,
.wakeup = 2,
};
static struct ddr_chipinfo sm1_ddr_chipinfo = {
.same_src_fn = true,
.ugt = true,
.src_sel_ctrl = true,
.asrc_src_sel_ctrl = true,
.fifo_num = 4,
.wakeup = 2,
};
static const struct of_device_id aml_ddr_mngr_device_id[] = {
{
.compatible = "amlogic, axg-audio-ddr-manager",
.data = &axg_ddr_chipinfo,
},
{
.compatible = "amlogic, g12a-audio-ddr-manager",
.data = &g12a_ddr_chipinfo,
},
{
.compatible = "amlogic, tl1-audio-ddr-manager",
.data = &tl1_ddr_chipinfo,
},
{
.compatible = "amlogic, sm1-audio-ddr-manager",
.data = &sm1_ddr_chipinfo,
},
{},
};
MODULE_DEVICE_TABLE(of, aml_ddr_mngr_device_id);
static bool pm_audio_in_suspend;
void pm_audio_set_suspend(bool is_suspend)
{
pm_audio_in_suspend = is_suspend;
}
bool pm_audio_is_suspend(void)
{
return pm_audio_in_suspend;
}
/* Detects a suspend and resume event */
static int ddr_pm_event(struct notifier_block *notifier,
unsigned long pm_event, void *unused)
{
pr_info("%s, pm_event:%lu\n", __func__, pm_event);
switch (pm_event) {
case PM_SUSPEND_PREPARE:
pm_audio_set_suspend(true);
break;
case PM_POST_SUSPEND:
break;
default:
break;
}
return NOTIFY_DONE;
}
static struct notifier_block ddr_pm_notifier_block = {
.notifier_call = ddr_pm_event,
};
static int aml_ddr_mngr_platform_probe(struct platform_device *pdev)
{
struct ddr_chipinfo *p_ddr_chipinfo;
int ddr_num = 3; /* early chipset support max 3 ddr num */
int i, ret;
p_ddr_chipinfo = (struct ddr_chipinfo *)
of_device_get_match_data(&pdev->dev);
if (!p_ddr_chipinfo)
dev_warn_once(&pdev->dev,
"check whether to update ddr_mngr chipinfo\n");
/* irqs */
toddrs[DDR_A].irq = platform_get_irq_byname(pdev, "toddr_a");
toddrs[DDR_B].irq = platform_get_irq_byname(pdev, "toddr_b");
toddrs[DDR_C].irq = platform_get_irq_byname(pdev, "toddr_c");
frddrs[DDR_A].irq = platform_get_irq_byname(pdev, "frddr_a");
frddrs[DDR_B].irq = platform_get_irq_byname(pdev, "frddr_b");
frddrs[DDR_C].irq = platform_get_irq_byname(pdev, "frddr_c");
if (p_ddr_chipinfo
&& (p_ddr_chipinfo->fifo_num == 4)) {
toddrs[DDR_D].irq = platform_get_irq_byname(pdev, "toddr_d");
frddrs[DDR_D].irq = platform_get_irq_byname(pdev, "frddr_d");
if (toddrs[DDR_D].irq < 0 || frddrs[DDR_D].irq < 0)
dev_err(&pdev->dev, "check irq for DDR_D\n");
ddr_num = p_ddr_chipinfo->fifo_num;
}
for (i = 0; i < ddr_num; i++) {
pr_info("%d, irqs toddr %d, frddr %d\n",
i, toddrs[i].irq, frddrs[i].irq);
if (toddrs[i].irq <= 0 || frddrs[i].irq <= 0) {
dev_err(&pdev->dev, "platform_get_irq_byname failed\n");
return -ENXIO;
}
}
/* inits */
toddrs[DDR_A].reg_base = EE_AUDIO_TODDR_A_CTRL0;
toddrs[DDR_B].reg_base = EE_AUDIO_TODDR_B_CTRL0;
toddrs[DDR_C].reg_base = EE_AUDIO_TODDR_C_CTRL0;
toddrs[DDR_A].fifo_id = DDR_A;
toddrs[DDR_B].fifo_id = DDR_B;
toddrs[DDR_C].fifo_id = DDR_C;
frddrs[DDR_A].reg_base = EE_AUDIO_FRDDR_A_CTRL0;
frddrs[DDR_B].reg_base = EE_AUDIO_FRDDR_B_CTRL0;
frddrs[DDR_C].reg_base = EE_AUDIO_FRDDR_C_CTRL0;
frddrs[DDR_A].fifo_id = DDR_A;
frddrs[DDR_B].fifo_id = DDR_B;
frddrs[DDR_C].fifo_id = DDR_C;
if (p_ddr_chipinfo) {
toddrs[DDR_A].chipinfo = p_ddr_chipinfo;
toddrs[DDR_B].chipinfo = p_ddr_chipinfo;
toddrs[DDR_C].chipinfo = p_ddr_chipinfo;
frddrs[DDR_A].chipinfo = p_ddr_chipinfo;
frddrs[DDR_B].chipinfo = p_ddr_chipinfo;
frddrs[DDR_C].chipinfo = p_ddr_chipinfo;
if (p_ddr_chipinfo->fifo_num == 4) {
toddrs[DDR_D].reg_base = EE_AUDIO_TODDR_D_CTRL0;
toddrs[DDR_D].fifo_id = DDR_D;
frddrs[DDR_D].reg_base = EE_AUDIO_FRDDR_D_CTRL0;
frddrs[DDR_D].fifo_id = DDR_D;
}
}
ret = register_pm_notifier(&ddr_pm_notifier_block);
if (ret)
pr_warn("[%s] failed to register PM notifier %d\n",
__func__, ret);
return 0;
}
struct platform_driver aml_audio_ddr_manager = {
.driver = {
.name = DRV_NAME,
.of_match_table = aml_ddr_mngr_device_id,
},
.probe = aml_ddr_mngr_platform_probe,
};
module_platform_driver(aml_audio_ddr_manager);
/* Module information */
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("ALSA Soc Aml Audio DDR Manager");
MODULE_LICENSE("GPL v2");