blob: 2b4d75231f2a128ce2a5f1b9b22d1edf3c1c0b62 [file] [log] [blame]
/*
* drivers/amlogic/audiodsp/dsp_codec.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 pr_fmt(fmt) "audio_dsp: " fmt
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/cache.h>
#include <asm/cacheflush.h>
#include <linux/amlogic/amports/ptsserv.h>
#include <linux/amlogic/amports/timestamp.h>
/* #include <asm/dsp/dsp_register.h> */
#include "dsp_microcode.h"
#include "audiodsp_module.h"
#include "dsp_control.h"
#include "dsp_mailbox.h"
#include "dsp_codec.h"
int dsp_codec_start(struct audiodsp_priv *priv)
{
return dsp_mailbox_send(priv, 1, M2B_IRQ2_DECODE_START, 0, 0, 0);
}
int dsp_codec_stop(struct audiodsp_priv *priv)
{
return dsp_mailbox_send(priv, 1, M2B_IRQ3_DECODE_STOP, 0, 0, 0);
}
int dsp_codec_get_bufer_data_len(struct audiodsp_priv *priv)
{
#define REVERSD_BYTES 32
#define CACHE_ALIGNED(x) (x&(~0x1f))
unsigned long rp, wp, len, flags;
local_irq_save(flags);
rp = dsp_codec_get_rd_addr(priv);
wp = dsp_codec_get_wd_addr(priv);
if (rp > wp)
len = priv->stream_buffer_size - (rp - wp);
else
len = (wp - rp);
len = (len > REVERSD_BYTES) ? (len - REVERSD_BYTES) : 0;
len = CACHE_ALIGNED(len);
local_irq_restore(flags);
return len;
}
int dsp_codec_get_bufer_data_len1(struct audiodsp_priv *priv,
unsigned long wd_ptr)
{
#define REVERSD_BYTES 32
#define CACHE_ALIGNED(x) (x&(~0x1f))
unsigned long rp, wp, len, flags;
local_irq_save(flags);
rp = dsp_codec_get_rd_addr(priv);
wp = ARC_2_ARM_ADDR_SWAP(wd_ptr);
if (rp > wp)
len = priv->stream_buffer_size - (rp - wp);
else
len = (wp - rp);
len = (len > REVERSD_BYTES) ? (len - REVERSD_BYTES) : 0;
len = CACHE_ALIGNED(len);
local_irq_restore(flags);
return len;
}
unsigned long dsp_codec_inc_rd_addr(struct audiodsp_priv *priv, int size)
{
unsigned long rd, flags;
local_irq_save(flags);
rd = dsp_codec_get_rd_addr(priv);
rd = rd + size;
if (rd >= priv->stream_buffer_end)
rd = rd - priv->stream_buffer_size;
DSP_WD(DSP_DECODE_OUT_RD_ADDR, ARM_2_ARC_ADDR_SWAP((void *)rd));
local_irq_restore(flags);
return rd;
}
u32 dsp_codec_get_current_pts(struct audiodsp_priv *priv)
{
#ifdef CONFIG_AM_PTSSERVER
u32 pts;
u32 delay_pts;
int len;
u64 frame_nums;
int res;
u32 offset, buffered_len, wp;
mutex_lock(&priv->stream_buffer_mutex);
if (priv->frame_format.channel_num == 0
|| priv->frame_format.sample_rate == 0
|| priv->frame_format.data_width == 0) {
pr_info("unvalid audio format!\n");
mutex_unlock(&priv->stream_buffer_mutex);
return -1;
}
#if 0
if (priv->stream_fmt == MCODEC_FMT_COOK) {
pts = priv->cur_frame_info.offset;
mutex_unlock(&priv->stream_buffer_mutex);
} else
#endif
{
buffered_len = DSP_RD(DSP_BUFFERED_LEN);
wp = DSP_RD(DSP_DECODE_OUT_WD_PTR);
offset = DSP_RD(DSP_AFIFO_RD_OFFSET1);
/* before audio start, the pts always be at the first index */
if (!timestamp_apts_started())
offset = 0;
if (priv->stream_fmt == MCODEC_FMT_COOK
|| priv->stream_fmt == MCODEC_FMT_RAAC) {
pts = DSP_RD(DSP_AFIFO_RD_OFFSET1);
res = 0;
} else {
res =
pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts,
300);
if (!priv->first_lookup_over) {
priv->first_lookup_over = 1;
if (first_lookup_pts_failed(PTS_TYPE_AUDIO)) {
priv->out_len_after_last_valid_pts = 0;
priv->last_valid_pts = pts;
mutex_unlock
(&priv->stream_buffer_mutex);
return pts;
}
}
}
if (res == 0) {
/* printk("check out pts == %x\n", pts); */
priv->out_len_after_last_valid_pts = 0;
len =
buffered_len + dsp_codec_get_bufer_data_len1(priv,
wp);
frame_nums =
(len * 8 /
(priv->frame_format.data_width *
priv->frame_format.channel_num));
delay_pts =
div64_u64(frame_nums * 90 * 20,
priv->frame_format.sample_rate / 50);
/* printk("cal delay pts == %x\n", delay_pts); */
if (pts > delay_pts)
pts -= delay_pts;
else
pts = 0;
priv->last_valid_pts = pts;
}
else if (priv->last_valid_pts >= 0) {
pts = priv->last_valid_pts;
len = priv->out_len_after_last_valid_pts;
frame_nums =
(len * 8 /
(priv->frame_format.data_width *
priv->frame_format.channel_num));
pts += div64_u64(frame_nums * 90 * 20,
priv->frame_format.sample_rate / 50);
}
else {
pr_info("checkout audio pts failed!\n");
pts = -1;
}
mutex_unlock(&priv->stream_buffer_mutex);
}
return pts;
#endif
return -1;
}