blob: 557eb9680f596b0b660d0b4e803e405db9654568 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
//#define DEBUG
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/extcon-provider.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <linux/workqueue.h>
#include <linux/amlogic/iomap.h>
#include <linux/amlogic/media/sound/hdmi_earc.h>
#include "resample.h"
#include "ddr_mngr.h"
#include "earc_hw.h"
#include "audio_utils.h"
#include "earc.h"
#include "frhdmirx_hw.h"
#include "sharebuffer.h"
#include "spdif_hw.h"
#include "../common/audio_uevent.h"
#if (defined CONFIG_AMLOGIC_MEDIA_TVIN_HDMI ||\
defined CONFIG_AMLOGIC_MEDIA_TVIN_HDMI_MODULE)
#include <linux/amlogic/media/frame_provider/tvin/tvin.h>
#endif
#define DRV_NAME "EARC"
#define EARCRX_DEFAULT_LATENCY 100
/*
* IEC958 controller(mixer) functions
*
* Channel status get/put control
* User bit value get/put control
* Valid bit value get control
* DPLL lock status get control
* User bit sync mode selection control
*/
static int IEC958_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
#define SND_IEC958(xname, xhandler_get, xhandler_put) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_PCM, \
.name = xname, \
.info = IEC958_info, \
.get = xhandler_get, \
.put = xhandler_put, \
}
enum work_event {
EVENT_NONE,
EVENT_RX_ANA_AUTO_CAL = 0x1 << 0,
EVENT_TX_ANA_AUTO_CAL = 0x1 << 1,
};
struct earc_chipinfo {
unsigned int earc_spdifout_lane_mask;
bool rx_dmac_sync_int;
bool rterm_on;
bool no_ana_auto_cal;
bool chnum_mult_mode;
bool unstable_tick_sel;
bool rx_enable;
bool tx_enable;
};
struct earc {
struct aml_audio_controller *actrl;
struct device *dev;
struct clk *clk_rx_cmdc;
struct clk *clk_rx_dmac;
struct clk *clk_rx_cmdc_srcpll;
struct clk *clk_rx_dmac_srcpll;
struct clk *clk_tx_gate;
struct clk *clk_tx_cmdc;
struct clk *clk_tx_dmac;
struct clk *clk_tx_cmdc_srcpll;
struct clk *clk_tx_dmac_srcpll;
struct regmap *tx_cmdc_map;
struct regmap *tx_dmac_map;
struct regmap *tx_top_map;
struct regmap *rx_cmdc_map;
struct regmap *rx_dmac_map;
struct regmap *rx_top_map;
struct toddr *tddr;
struct frddr *fddr;
int irq_earc_rx;
int irq_earc_tx;
/* external connect */
struct extcon_dev *rx_edev;
/* audio codec type for tx */
enum audio_coding_types tx_audio_coding_type;
/* freq for tx dmac clk */
int tx_dmac_freq;
/* whether dmac clock is on */
bool rx_dmac_clk_on;
spinlock_t rx_lock; /* rx dmac clk lock */
bool tx_dmac_clk_on;
/* do analog auto calibration when bootup */
struct work_struct work;
enum work_event event;
bool rx_bootup_auto_cal;
bool tx_bootup_auto_cal;
struct earc_chipinfo *chipinfo;
/* mute in channel status */
bool rx_cs_mute;
bool tx_cs_mute;
/* Channel Allocation */
unsigned int tx_cs_lpcm_ca;
/* channel map */
struct snd_pcm_chmap *rx_chmap;
struct snd_pcm_chmap *tx_chmap;
struct snd_pcm_substream *substreams[2];
struct work_struct rx_dmac_int_work;
struct work_struct tx_resume_work;
u8 cds_data[CDS_MAX_BYTES];
enum sharebuffer_srcs samesource_sel;
struct samesource_info ss_info;
bool earctx_on;
/*
* The device(eARC Rx) type, our chip is eARC Tx
* ATNDTYP_DISCNCT: For nothing device connected
* ATNDTYP_ARC: The device(eARC Rx) is ARC device
* ATNDTYP_EARC: The device(eARC Rx) is eARC device
*/
enum attend_type earctx_connected_device_type;
unsigned int rx_status0;
unsigned int rx_status1;
bool tx_earc_mode;
bool tx_reset_hpd;
};
static struct earc *s_earc;
bool is_earc_spdif(void)
{
return !!s_earc;
}
bool get_earcrx_chnum_mult_mode(void)
{
if (s_earc && s_earc->chipinfo)
return s_earc->chipinfo->chnum_mult_mode;
return false;
}
#define EARC_BUFFER_BYTES (512 * 1024)
#define EARC_RATES (SNDRV_PCM_RATE_8000_192000)
#define EARC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_pcm_hardware earc_hardware = {
.info =
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE,
.formats = EARC_FORMATS,
.period_bytes_min = 64,
.period_bytes_max = 128 * 1024,
.periods_min = 2,
.periods_max = 1024,
.buffer_bytes_max = EARC_BUFFER_BYTES,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
.channels_max = 32,
};
bool aml_get_earctx_enable(void)
{
if (s_earc)
return get_earctx_enable(s_earc->tx_cmdc_map, s_earc->tx_dmac_map);
return false;
}
enum attend_type aml_get_earctx_connected_device_type(void)
{
if (!s_earc)
return -ENOTCONN;
return s_earc->earctx_connected_device_type;
}
bool aml_get_earctx_reset_hpd(void)
{
if (!s_earc)
return 0;
return s_earc->tx_reset_hpd;
}
void aml_earctx_enable_d2a(int enable)
{
if (!s_earc)
return;
return earctx_enable_d2a(s_earc->tx_top_map, enable);
}
static irqreturn_t earc_ddr_isr(int irq, void *data)
{
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)data;
if (!snd_pcm_running(substream))
return IRQ_HANDLED;
snd_pcm_period_elapsed(substream);
return IRQ_HANDLED;
}
static void earcrx_update_attend_event(struct earc *p_earc,
bool is_earc, bool state)
{
if (state) {
if (is_earc) {
extcon_set_state_sync(p_earc->rx_edev,
EXTCON_EARCRX_ATNDTYP_ARC, false);
extcon_set_state_sync(p_earc->rx_edev,
EXTCON_EARCRX_ATNDTYP_EARC, state);
} else {
extcon_set_state_sync(p_earc->rx_edev,
EXTCON_EARCRX_ATNDTYP_ARC, state);
extcon_set_state_sync(p_earc->rx_edev,
EXTCON_EARCRX_ATNDTYP_EARC, false);
}
} else {
extcon_set_state_sync(p_earc->rx_edev,
EXTCON_EARCRX_ATNDTYP_ARC, state);
extcon_set_state_sync(p_earc->rx_edev,
EXTCON_EARCRX_ATNDTYP_EARC, state);
}
}
static void earcrx_pll_reset(struct earc *p_earc)
{
int i = 0, count = 0;
earcrx_dmac_sync_int_enable(p_earc->rx_top_map, 0);
earcrx_pll_refresh(p_earc->rx_top_map,
RST_BY_SELF,
true);
/* wait pll valid */
usleep_range(350, 400);
/* bit 31 is 1, and bit 30 is 0, then need reset pll */
while (earxrx_get_pll_valid(p_earc->rx_top_map) &&
!earxrx_get_pll_valid_auto(p_earc->rx_top_map)) {
earcrx_pll_refresh(p_earc->rx_top_map,
RST_BY_SELF,
true);
pr_info("refresh earcrx pll, i %d\n", i++);
if (i >= 9) {
dev_info(p_earc->dev,
"refresh earcrx pll failed\n");
break;
}
usleep_range(350, 400);
}
/* there are 5 times continues bit 31 and bit 30 is 1 */
i = 0;
while (true) {
if (earxrx_get_pll_valid(p_earc->rx_top_map) &&
earxrx_get_pll_valid_auto(p_earc->rx_top_map)) {
i++;
} else {
i = 0;
count++;
}
if (i >= 5 || count >= 2)
break;
usleep_range(45, 50);
}
i = 0;
while (earcrx_dmac_get_irqs(p_earc->rx_top_map) & 0x10000000) {
earcrx_dmac_sync_clr_irqs(p_earc->rx_top_map);
i++;
if (i > 5)
break;
usleep_range(45, 50);
}
earcrx_dmac_sync_int_enable(p_earc->rx_top_map, 1);
pr_info("earcrx pll %d, auto %d, pengding is 0x%x\n",
earxrx_get_pll_valid(p_earc->rx_top_map),
earxrx_get_pll_valid_auto(p_earc->rx_top_map),
earcrx_dmac_get_irqs(p_earc->rx_top_map));
}
static void valid_auto_work_func(struct work_struct *p_work)
{
struct earc *p_earc = container_of(p_work, struct earc, rx_dmac_int_work);
earcrx_pll_reset(p_earc);
}
static irqreturn_t rx_handler(int irq, void *data)
{
struct earc *p_earc = (struct earc *)data;
unsigned int mask = earcrx_dmac_get_mask(p_earc->rx_top_map);
p_earc->rx_status0 = earcrx_cdmc_get_irqs(p_earc->rx_top_map);
if (p_earc->rx_status0)
earcrx_cdmc_clr_irqs(p_earc->rx_top_map, p_earc->rx_status0);
p_earc->rx_status1 = earcrx_dmac_get_irqs(p_earc->rx_top_map);
p_earc->rx_status1 = p_earc->rx_status1 & mask;
if (p_earc->rx_status1)
earcrx_dmac_clr_irqs(p_earc->rx_top_map, p_earc->rx_status1);
return IRQ_WAKE_THREAD;
}
static irqreturn_t earc_rx_isr(int irq, void *data)
{
struct earc *p_earc = (struct earc *)data;
if (p_earc->rx_status0 & INT_EARCRX_CMDC_TIMEOUT) {
earcrx_update_attend_event(p_earc,
false, false);
dev_info(p_earc->dev, "EARCRX_CMDC_TIMEOUT\n");
}
if (p_earc->rx_status0 & INT_EARCRX_CMDC_IDLE2) {
earcrx_update_attend_event(p_earc,
false, true);
dev_info(p_earc->dev, "EARCRX_CMDC_IDLE2\n");
}
if (p_earc->rx_status0 & INT_EARCRX_CMDC_IDLE1) {
earcrx_update_attend_event(p_earc,
false, false);
dev_info(p_earc->dev, "EARCRX_CMDC_IDLE1\n");
}
if (p_earc->rx_status0 & INT_EARCRX_CMDC_DISC2)
dev_info(p_earc->dev, "EARCRX_CMDC_DISC2\n");
if (p_earc->rx_status0 & INT_EARCRX_CMDC_DISC1)
dev_info(p_earc->dev, "EARCRX_CMDC_DISC1\n");
if (p_earc->rx_status0 & INT_EARCRX_CMDC_EARC) {
u8 latency = EARCRX_DEFAULT_LATENCY;
earcrx_cmdc_set_latency(p_earc->rx_cmdc_map, &latency);
earcrx_cmdc_set_cds(p_earc->rx_cmdc_map, p_earc->cds_data);
earcrx_update_attend_event(p_earc,
true, true);
dev_info(p_earc->dev, "EARCRX_CMDC_EARC\n");
}
if (p_earc->rx_status0 & INT_EARCRX_CMDC_LOSTHB)
dev_info(p_earc->dev, "EARCRX_CMDC_LOSTHB\n");
if (p_earc->rx_status0 & INT_EARCRX_CMDC_STATUS_CH)
dev_info(p_earc->dev, "EARCRX_CMDC_STATUS_CH\n");
if (p_earc->rx_dmac_clk_on) {
if (p_earc->rx_status1 & INT_EARCRX_ANA_RST_C_EARCRX_DIV2_HOLD_SET)
dev_info(p_earc->dev, "EARCRX_ANA_RST_C_EARCRX_DIV2_HOLD_SET\n");
if (p_earc->rx_status1 & INT_EARCRX_ANA_RST_C_NEW_FORMAT_SET) {
dev_info(p_earc->dev, "EARCRX_ANA_RST_C_NEW_FORMAT_SET\n");
earcrx_pll_refresh(p_earc->rx_top_map,
RST_BY_SELF, true);
}
if (p_earc->rx_status1 & INT_EARCRX_ERR_CORRECT_C_BCHERR_INT_SET)
dev_info(p_earc->dev, "EARCRX_ERR_CORRECT_BCHERR\n");
if (p_earc->rx_status1 & INT_ARCRX_BIPHASE_DECODE_R_PARITY_ERR)
dev_info(p_earc->dev, "ARCRX_R_PARITY_ERR\n");
if (p_earc->rx_status0 & INT_EARCRX_CMDC_HB_STATUS)
dev_dbg(p_earc->dev, "EARCRX_CMDC_HB_STATUS\n");
if (p_earc->rx_status1 & INT_ARCRX_BIPHASE_DECODE_C_FIND_PAPB)
dev_dbg(p_earc->dev, "ARCRX_C_FIND_PAPB\n");
if (p_earc->rx_status1 & INT_ARCRX_BIPHASE_DECODE_C_VALID_CHANGE)
dev_dbg(p_earc->dev, "ARCRX_C_VALID_CHANGE\n");
if (p_earc->rx_status1 & INT_ARCRX_BIPHASE_DECODE_C_FIND_NONPCM2PCM)
dev_dbg(p_earc->dev, "ARCRX_C_FIND_NONPCM2PCM\n");
if (p_earc->rx_status1 & INT_ARCRX_BIPHASE_DECODE_C_PCPD_CHANGE)
dev_dbg(p_earc->dev, "ARCRX_C_PCPD_CHANGE\n");
if (p_earc->rx_status1 & INT_ARCRX_BIPHASE_DECODE_C_CH_STATUS_CHANGE) {
int mute = earcrx_get_cs_mute(p_earc->rx_dmac_map);
if (p_earc->rx_cs_mute != mute) {
p_earc->rx_cs_mute = mute;
earcrx_pll_refresh(p_earc->rx_top_map,
RST_BY_SELF, true);
}
}
if (p_earc->rx_status1 & INT_ARCRX_BIPHASE_DECODE_I_SAMPLE_MODE_CHANGE)
dev_dbg(p_earc->dev, "ARCRX_I_SAMPLE_MODE_CHANGE\n");
if (p_earc->chipinfo->rx_dmac_sync_int &&
p_earc->rx_status1 & INT_EARCRX_DMAC_VALID_AUTO_NEG_INT_SET) {
earcrx_dmac_sync_int_enable(p_earc->rx_top_map, 0);
schedule_work(&p_earc->rx_dmac_int_work);
pr_info("%s EARCRX_DMAC_VALID_AUTO_NEG_INT_SET\n", __func__);
} else if (p_earc->chipinfo->unstable_tick_sel &&
p_earc->rx_status1 & INT_EARCRX_DMAC_VALID_AUTO_NEG_INT_SET) {
earcrx_dmac_sync_clr_irqs(p_earc->rx_top_map);
pr_info("%s unstable tick happen\n", __func__);
earcrx_pll_refresh(p_earc->rx_top_map, RST_BY_SELF, true);
}
}
return IRQ_HANDLED;
}
static void earctx_update_attend_event(struct earc *p_earc,
bool is_earc, bool state)
{
if (state) {
if (is_earc) {
p_earc->earctx_connected_device_type = ATNDTYP_EARC;
audio_send_uevent(p_earc->dev, EARCTX_ATNDTYP_EVENT, ATNDTYP_EARC);
} else {
/* if the connected device is earc, lost HB still is earc device */
if (p_earc->earctx_connected_device_type != ATNDTYP_EARC)
p_earc->earctx_connected_device_type = ATNDTYP_ARC;
audio_send_uevent(p_earc->dev, EARCTX_ATNDTYP_EVENT, ATNDTYP_ARC);
}
} else {
audio_send_uevent(p_earc->dev, EARCTX_ATNDTYP_EVENT, ATNDTYP_DISCNCT);
}
}
static irqreturn_t earc_tx_isr(int irq, void *data)
{
struct earc *p_earc = (struct earc *)data;
unsigned int status0 = earctx_cdmc_get_irqs(p_earc->tx_top_map);
struct snd_pcm_substream *substream =
p_earc->substreams[SNDRV_PCM_STREAM_PLAYBACK];
unsigned long flags;
if (status0)
earctx_cdmc_clr_irqs(p_earc->tx_top_map, status0);
if (substream) {
snd_pcm_stream_lock_irqsave(substream, flags);
if (snd_pcm_running(substream) &&
(earctx_cmdc_get_attended_type(p_earc->tx_cmdc_map)
== ATNDTYP_DISCNCT)) {
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
pr_info("snd_pcm_stop, eARC disconnect\n");
}
snd_pcm_stream_unlock_irqrestore(substream, flags);
}
if (status0 & INT_EARCTX_CMDC_EARC) {
earctx_update_attend_event(p_earc,
true, true);
p_earc->tx_reset_hpd = false;
dev_info(p_earc->dev, "EARCTX_CMDC_EARC\n");
}
if (status0 & INT_EARCTX_CMDC_DISC1)
dev_info(p_earc->dev, "EARCTX_CMDC_DISC1\n");
if (status0 & INT_EARCTX_CMDC_DISC2)
dev_info(p_earc->dev, "INT_EARCTX_CMDC_DISC2\n");
if (status0 & INT_EARCTX_CMDC_STATUS_CH)
dev_info(p_earc->dev, "EARCTX_CMDC_STATUS_CH\n");
if (status0 & INT_EARCTX_CMDC_HB_STATUS)
dev_dbg(p_earc->dev, "EARCTX_CMDC_HB_STATUS\n");
if (status0 & INT_EARCTX_CMDC_LOSTHB)
dev_info(p_earc->dev, "EARCTX_CMDC_LOSTHB\n");
if (status0 & INT_EARCTX_CMDC_TIMEOUT) {
dev_info(p_earc->dev, "EARCTX_CMDC_TIMEOUT\n");
earctx_cmdc_arc_connect(p_earc->tx_cmdc_map, true);
p_earc->tx_reset_hpd = false;
}
if (status0 & INT_EARCTX_CMDC_RECV_NACK)
dev_dbg(p_earc->dev, "EARCTX_CMDC_RECV_NACK\n");
if (status0 & INT_EARCTX_CMDC_RECV_NORSP)
dev_dbg(p_earc->dev, "EARCTX_CMDC_RECV_NORSP\n");
if (status0 & INT_EARCTX_CMDC_RECV_UNEXP)
dev_info(p_earc->dev, "EARCTX_CMDC_RECV_UNEXP\n");
/*
* plug out ARC, from ARC to IDLE2, then to IDLE1
* if receive both interrupt, then only send IDLE1 status
*/
if ((status0 & INT_EARCTX_CMDC_IDLE1) && (status0 & INT_EARCTX_CMDC_IDLE2)) {
earctx_update_attend_event(p_earc, false, false);
dev_info(p_earc->dev, "only send EARCTX_CMDC_IDLE1\n");
} else {
if (status0 & INT_EARCTX_CMDC_IDLE1) {
earctx_update_attend_event(p_earc, false, false);
dev_info(p_earc->dev, "EARCTX_CMDC_IDLE1\n");
}
if (status0 & INT_EARCTX_CMDC_IDLE2) {
earctx_update_attend_event(p_earc, false, true);
dev_info(p_earc->dev, "EARCTX_CMDC_IDLE2\n");
}
}
if (p_earc->tx_dmac_clk_on) {
unsigned int status1 = earctx_dmac_get_irqs(p_earc->tx_top_map);
if (status1)
earctx_dmac_clr_irqs(p_earc->tx_top_map, status1);
if (status1 & INT_EARCTX_FEM_C_HOLD_CLR)
dev_dbg(p_earc->dev, "EARCTX_FEM_C_HOLD_CLR\n");
if (status1 & INT_EARCTX_FEM_C_HOLD_START)
dev_dbg(p_earc->dev, "EARCTX_FEM_C_HOLD_START\n");
if (status1 & INT_EARCTX_ERRCORR_C_FIFO_THD_LESS_PASS)
dev_dbg(p_earc->dev, "EARCTX_ECCFIFO_OVERFLOW\n");
if (status1 & INT_EARCTX_ERRCORR_C_FIFO_OVERFLOW)
dev_dbg(p_earc->dev, "EARCTX_ECC_FIFO_OVERFLOW\n");
if (status1 & INT_EARCTX_ERRCORR_C_FIFO_EMPTY)
dev_dbg(p_earc->dev, "EARCTX_ECC_FIFO_EMPTY\n");
}
return IRQ_HANDLED;
}
static int earc_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct device *dev = rtd->cpu_dai->dev;
struct earc *p_earc;
int ret = 0;
p_earc = (struct earc *)dev_get_drvdata(dev);
snd_soc_set_runtime_hwparams(substream, &earc_hardware);
snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
dev, EARC_BUFFER_BYTES / 2, EARC_BUFFER_BYTES);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* select hdmirx arc source from earctx spdif */
arc_earc_source_select(EARCTX_SPDIF_TO_HDMIRX);
p_earc->fddr = aml_audio_register_frddr(dev,
p_earc->actrl,
earc_ddr_isr, substream, false);
if (!p_earc->fddr) {
ret = -ENXIO;
dev_err(dev, "failed to claim frddr\n");
goto err_ddr;
}
p_earc->earctx_on = true;
} else {
p_earc->tddr = aml_audio_register_toddr(dev,
p_earc->actrl,
earc_ddr_isr, substream);
if (!p_earc->tddr) {
ret = -ENXIO;
dev_err(dev, "failed to claim toddr\n");
goto err_ddr;
}
}
runtime->private_data = p_earc;
return 0;
err_ddr:
snd_pcm_lib_preallocate_free(substream);
return ret;
}
static int earc_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct earc *p_earc = runtime->private_data;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
p_earc->earctx_on = false;
aml_audio_unregister_frddr(p_earc->dev, substream);
} else {
aml_audio_unregister_toddr(p_earc->dev, substream);
}
runtime->private_data = NULL;
snd_pcm_lib_preallocate_free(substream);
return 0;
}
static int earc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int earc_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_lib_free_pages(substream);
return 0;
}
static int earc_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct earc *p_earc = runtime->private_data;
unsigned int start_addr, end_addr, int_addr;
unsigned int period, threshold;
start_addr = runtime->dma_addr;
end_addr = start_addr + runtime->dma_bytes - FIFO_BURST;
period = frames_to_bytes(runtime, runtime->period_size);
int_addr = period / FIFO_BURST;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
struct frddr *fr = p_earc->fddr;
/*
* Contrast minimum of period and fifo depth,
* and set the value as half.
*/
threshold = min(period, fr->chipinfo->fifo_depth);
threshold /= 2;
/* Use all the fifo */
aml_frddr_set_fifos(fr, fr->chipinfo->fifo_depth, threshold);
aml_frddr_set_buf(fr, start_addr, end_addr);
aml_frddr_set_intrpt(fr, int_addr);
} else {
struct toddr *to = p_earc->tddr;
/*
* Contrast minimum of period and fifo depth,
* and set the value as half.
*/
threshold = min(period, to->chipinfo->fifo_depth);
threshold /= 2;
aml_toddr_set_fifos(to, threshold);
aml_toddr_set_buf(to, start_addr, end_addr);
aml_toddr_set_intrpt(to, int_addr);
}
return 0;
}
static snd_pcm_uframes_t earc_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct earc *p_earc = runtime->private_data;
unsigned int addr, start_addr;
snd_pcm_uframes_t frames;
start_addr = runtime->dma_addr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
addr = aml_frddr_get_position(p_earc->fddr);
else
addr = aml_toddr_get_position(p_earc->tddr);
frames = bytes_to_frames(runtime, addr - start_addr);
if (frames > runtime->buffer_size)
frames = 0;
return frames;
}
static int earc_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
return snd_pcm_lib_default_mmap(substream, vma);
}
static struct snd_pcm_ops earc_ops = {
.open = earc_open,
.close = earc_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = earc_hw_params,
.hw_free = earc_hw_free,
.prepare = earc_prepare,
.pointer = earc_pointer,
.mmap = earc_mmap,
};
static int earc_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int bit_depth = snd_pcm_format_width(runtime->format);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
struct frddr *fr = p_earc->fddr;
enum frddr_dest dst = EARCTX_DMAC;
unsigned int fifo_id;
enum attend_type type =
earctx_cmdc_get_attended_type(p_earc->tx_cmdc_map);
struct iec_cnsmr_cs cs_info;
if (type == ATNDTYP_DISCNCT) {
dev_err(p_earc->dev,
"Neither eARC_TX or ARC_TX is attended!\n");
return -ENOTCONN;
}
dev_info(p_earc->dev,
"%s connected, Expected frddr dst:%s\n",
(type == ATNDTYP_ARC) ? "ARC" : "eARC",
frddr_src_get_str(dst));
fifo_id = aml_frddr_get_fifo_id(fr);
aml_frddr_set_format(fr,
runtime->channels,
runtime->rate,
bit_depth - 1,
spdifout_get_frddr_type(bit_depth));
aml_frddr_select_dst(fr, dst);
earctx_dmac_init(p_earc->tx_top_map,
p_earc->tx_dmac_map,
p_earc->chipinfo->earc_spdifout_lane_mask,
3,
0x10);
earctx_dmac_set_format(p_earc->tx_dmac_map,
fr->fifo_id,
bit_depth - 1,
spdifout_get_frddr_type(bit_depth));
iec_get_cnsmr_cs_info(&cs_info,
p_earc->tx_audio_coding_type,
runtime->channels,
runtime->rate);
earctx_set_cs_info(p_earc->tx_dmac_map,
p_earc->tx_audio_coding_type,
&cs_info,
&p_earc->tx_cs_lpcm_ca);
earctx_set_cs_mute(p_earc->tx_dmac_map, p_earc->tx_cs_mute);
} else {
struct toddr *to = p_earc->tddr;
unsigned int msb = 0, lsb = 0, toddr_type = 0;
unsigned int src = EARCRX_DMAC;
struct toddr_fmt fmt;
enum attend_type type =
earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map);
if (type == ATNDTYP_DISCNCT) {
dev_err(p_earc->dev,
"Neither eARC_RX or ARC_RX is attended!\n");
return -ENOTCONN;
}
dev_info(p_earc->dev,
"%s connected, Expected toddr src:%s\n",
(type == ATNDTYP_ARC) ? "ARC" : "eARC",
toddr_src_get_str(src));
if (bit_depth == 32)
toddr_type = 3;
else if (bit_depth == 24)
toddr_type = 4;
else
toddr_type = 0;
msb = 28 - 1;
lsb = (bit_depth == 16) ? 28 - bit_depth : 4;
if (get_resample_version() >= T5_RESAMPLE &&
get_resample_source(RESAMPLE_A) == EARCRX_DMAC) {
msb = 32 - 1;
lsb = 32 - bit_depth;
}
fmt.type = toddr_type;
fmt.msb = msb;
fmt.lsb = lsb;
fmt.endian = 0;
fmt.bit_depth = bit_depth;
fmt.ch_num = runtime->channels;
fmt.rate = runtime->rate;
aml_toddr_select_src(to, src);
aml_toddr_set_format(to, &fmt);
earcrx_dmac_init(p_earc->rx_top_map,
p_earc->rx_dmac_map,
p_earc->chipinfo->unstable_tick_sel,
p_earc->chipinfo->chnum_mult_mode);
earcrx_arc_init(p_earc->rx_dmac_map);
}
return 0;
}
static void earctx_update_clk(struct earc *p_earc,
unsigned int channels,
unsigned int rate)
{
unsigned int multi = audio_multi_clk(p_earc->tx_audio_coding_type);
unsigned int freq = rate * 128 * EARC_DMAC_MUTIPLIER * multi;
unsigned int mpll_freq = freq *
mpll2dmac_clk_ratio_by_type(p_earc->tx_audio_coding_type);
/* make sure mpll_freq doesn't exceed MPLL max freq */
while (mpll_freq > AML_MPLL_FREQ_MAX)
mpll_freq = mpll_freq >> 1;
dev_info(p_earc->dev, "set %dX normal dmac clk, p_earc->tx_dmac_freq:%d\n",
multi, p_earc->tx_dmac_freq);
dev_info(p_earc->dev,
"%s, rate:%d, set freq:%d, get freq:%lu, set src freq:%d, get src freq:%lu\n",
__func__,
rate,
freq,
clk_get_rate(p_earc->clk_tx_dmac),
mpll_freq,
clk_get_rate(p_earc->clk_tx_dmac_srcpll));
if (freq == p_earc->tx_dmac_freq)
return;
/* same with tdm mpll */
clk_set_rate(p_earc->clk_tx_dmac_srcpll, mpll_freq);
p_earc->tx_dmac_freq = freq;
clk_set_rate(p_earc->clk_tx_dmac, freq);
}
int spdif_codec_to_earc_codec[][2] = {
{AUD_CODEC_TYPE_STEREO_PCM, AUDIO_CODING_TYPE_STEREO_LPCM},
{AUD_CODEC_TYPE_DTS_RAW_MODE, AUDIO_CODING_TYPE_DTS},
{AUD_CODEC_TYPE_AC3, AUDIO_CODING_TYPE_AC3},
{AUD_CODEC_TYPE_DTS, AUDIO_CODING_TYPE_DTS},
{AUD_CODEC_TYPE_EAC3, AUDIO_CODING_TYPE_EAC3},
{AUD_CODEC_TYPE_DTS_HD, AUDIO_CODING_TYPE_DTS_HD},
{AUD_CODEC_TYPE_MULTI_LPCM, AUDIO_CODING_TYPE_MULTICH_2CH_LPCM},
{AUD_CODEC_TYPE_TRUEHD, AUDIO_CODING_TYPE_MLP},
{AUD_CODEC_TYPE_DTS_HD_MA, AUDIO_CODING_TYPE_DTS_HD_MA},
{AUD_CODEC_TYPE_HSR_STEREO_PCM, AUDIO_CODING_TYPE_STEREO_LPCM},
{AUD_CODEC_TYPE_AC3_LAYOUT_B, AUDIO_CODING_TYPE_AC3_LAYOUT_B},
{AUD_CODEC_TYPE_OBA, AUDIO_CODING_TYPE_HBR_LPCM}
};
int aml_earctx_set_audio_coding_type(enum audio_coding_types new_coding_type)
{
struct frddr *fr;
enum attend_type type;
struct iec_cnsmr_cs cs_info;
int channels, rate;
if (!s_earc || IS_ERR(s_earc->tx_cmdc_map))
return 0;
s_earc->tx_audio_coding_type = new_coding_type;
if (!s_earc->tx_dmac_clk_on)
return 0;
dev_info(s_earc->dev, "tx audio coding type: 0x%02x\n", new_coding_type);
type = earctx_cmdc_get_attended_type(s_earc->tx_cmdc_map);
fr = s_earc->fddr;
/* Update dmac clk ? */
if (s_earc->earctx_on) {
channels = fr->channels;
rate = fr->rate;
} else {
channels = s_earc->ss_info.channels;
rate = s_earc->ss_info.rate;
}
earctx_update_clk(s_earc, channels, rate);
/* Update ECC enable/disable */
earctx_compressed_enable(s_earc->tx_dmac_map,
type, new_coding_type, true);
/* Update Channel Status in runtime */
iec_get_cnsmr_cs_info(&cs_info,
s_earc->tx_audio_coding_type,
channels,
rate);
earctx_set_cs_info(s_earc->tx_dmac_map,
s_earc->tx_audio_coding_type,
&cs_info,
&s_earc->tx_cs_lpcm_ca);
return 0;
}
int sharebuffer_earctx_prepare(struct snd_pcm_substream *substream,
struct frddr *fr, enum aud_codec_types type, int lane_i2s)
{
enum attend_type earc_type;
struct snd_pcm_runtime *runtime = substream->runtime;
int bit_depth = snd_pcm_format_width(runtime->format);
int i, ret;
unsigned int chmask = 0, swap_masks = 0;
if (!s_earc)
return -ENOTCONN;
earc_type = earctx_cmdc_get_attended_type(s_earc->tx_cmdc_map);
if (earc_type == ATNDTYP_DISCNCT) {
dev_dbg(s_earc->dev, "%s: Neither eARC_TX or ARC_TX is attended!\n", __func__);
return -ENOTCONN;
}
if (IS_ERR(s_earc->clk_tx_dmac) || IS_ERR(s_earc->clk_tx_dmac_srcpll))
return -ENOTCONN;
/* tx dmac clk */
ret = clk_prepare_enable(s_earc->clk_tx_dmac);
if (ret) {
dev_err(s_earc->dev, "Can't enable earc clk_tx_dmac: %d\n", ret);
return ret;
}
s_earc->tx_dmac_clk_on = true;
ret = clk_prepare_enable(s_earc->clk_tx_dmac_srcpll);
if (ret) {
dev_err(s_earc->dev, "Can't enable earc clk_tx_dmac_srcpll:%d\n", ret);
return ret;
}
/* same source channels always 2 */
s_earc->ss_info.channels = 2;
s_earc->ss_info.rate = runtime->rate;
aml_earctx_set_audio_coding_type(spdif_codec_to_earc_codec[type][1]);
for (i = 0; i < runtime->channels; i++)
chmask |= (1 << i);
swap_masks = (2 * lane_i2s) | (2 * lane_i2s + 1) << 4;
earctx_dmac_init(s_earc->tx_top_map,
s_earc->tx_dmac_map,
s_earc->chipinfo->earc_spdifout_lane_mask,
chmask,
swap_masks);
earctx_dmac_set_format(s_earc->tx_dmac_map,
fr->fifo_id,
bit_depth - 1,
spdifout_get_frddr_type(bit_depth));
earctx_set_cs_mute(s_earc->tx_dmac_map, s_earc->tx_cs_mute);
return 0;
}
void aml_earctx_enable(bool enable)
{
if (!s_earc || !s_earc->tx_dmac_clk_on)
return;
earctx_enable(s_earc->tx_top_map,
s_earc->tx_cmdc_map,
s_earc->tx_dmac_map,
s_earc->tx_audio_coding_type,
enable,
s_earc->chipinfo->rterm_on);
}
static int earc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *cpu_dai)
{
struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev_info(substream->pcm->card->dev, "eARC/ARC TX enable\n");
aml_frddr_enable(p_earc->fddr, true);
earctx_enable(p_earc->tx_top_map,
p_earc->tx_cmdc_map,
p_earc->tx_dmac_map,
p_earc->tx_audio_coding_type,
true,
p_earc->chipinfo->rterm_on);
} else {
dev_info(substream->pcm->card->dev, "eARC/ARC RX enable\n");
aml_toddr_enable(p_earc->tddr, true);
earcrx_enable(p_earc->rx_cmdc_map,
p_earc->rx_dmac_map,
true);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev_info(substream->pcm->card->dev, "eARC/ARC TX disable\n");
earctx_enable(p_earc->tx_top_map,
p_earc->tx_cmdc_map,
p_earc->tx_dmac_map,
p_earc->tx_audio_coding_type,
false,
p_earc->chipinfo->rterm_on);
aml_frddr_enable(p_earc->fddr, false);
} else {
dev_info(substream->pcm->card->dev, "eARC/ARC RX disable\n");
earcrx_enable(p_earc->rx_cmdc_map,
p_earc->rx_dmac_map,
false);
aml_toddr_enable(p_earc->tddr, false);
}
break;
default:
return -EINVAL;
}
return 0;
}
static int earc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
earctx_update_clk(p_earc, channels, rate);
return 0;
}
static int earc_dai_set_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai);
if (clk_id == 1) {
/* RX */
clk_set_rate(p_earc->clk_rx_dmac, 500000000);
dev_info(p_earc->dev,
"earc rx cmdc clk:%lu rx dmac clk:%lu\n",
clk_get_rate(p_earc->clk_rx_cmdc),
clk_get_rate(p_earc->clk_rx_dmac));
}
return 0;
}
static int earc_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai);
int ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (IS_ERR(p_earc->clk_tx_dmac) || IS_ERR(p_earc->clk_tx_dmac_srcpll))
return -ENOTCONN;
/* tx dmac clk */
ret = clk_prepare_enable(p_earc->clk_tx_dmac);
if (ret) {
dev_err(p_earc->dev, "Can't enable earc clk_tx_dmac: %d\n", ret);
goto err;
}
p_earc->tx_dmac_clk_on = true;
ret = clk_prepare_enable(p_earc->clk_tx_dmac_srcpll);
if (ret) {
dev_err(p_earc->dev, "Can't enable earc clk_tx_dmac_srcpll:%d\n", ret);
goto err;
}
} else {
unsigned long flags;
if (IS_ERR(p_earc->clk_rx_dmac) || IS_ERR(p_earc->clk_rx_dmac_srcpll))
return -ENOTCONN;
/* rx dmac clk */
ret = clk_prepare_enable(p_earc->clk_rx_dmac);
if (ret) {
dev_err(p_earc->dev, "Can't enable earc clk_rx_dmac: %d\n", ret);
goto err;
}
spin_lock_irqsave(&p_earc->rx_lock, flags);
p_earc->rx_dmac_clk_on = true;
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
ret = clk_prepare_enable(p_earc->clk_rx_dmac_srcpll);
if (ret) {
dev_err(p_earc->dev, "Can't enable earc clk_rx_dmac_srcpll: %d\n", ret);
goto err;
}
earcrx_pll_refresh(p_earc->rx_top_map, RST_BY_SELF, true);
}
p_earc->substreams[substream->stream] = substream;
return 0;
err:
dev_err(p_earc->dev, "failed enable clock\n");
return -EINVAL;
}
static void earc_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!IS_ERR(p_earc->clk_tx_dmac)) {
clk_disable_unprepare(p_earc->clk_tx_dmac);
p_earc->tx_dmac_clk_on = false;
p_earc->tx_dmac_freq = 0;
}
if (!IS_ERR(p_earc->clk_tx_dmac_srcpll))
clk_disable_unprepare(p_earc->clk_tx_dmac_srcpll);
} else {
if (!IS_ERR(p_earc->clk_rx_dmac)) {
unsigned long flags;
spin_lock_irqsave(&p_earc->rx_lock, flags);
p_earc->rx_dmac_clk_on = false;
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
clk_disable_unprepare(p_earc->clk_rx_dmac);
}
if (!IS_ERR(p_earc->clk_rx_dmac_srcpll))
clk_disable_unprepare(p_earc->clk_rx_dmac_srcpll);
}
p_earc->substreams[substream->stream] = NULL;
}
static struct snd_soc_dai_ops earc_dai_ops = {
.prepare = earc_dai_prepare,
.trigger = earc_dai_trigger,
.hw_params = earc_dai_hw_params,
.set_sysclk = earc_dai_set_sysclk,
.startup = earc_dai_startup,
.shutdown = earc_dai_shutdown,
};
static struct snd_soc_dai_driver earc_dai[] = {
{
.name = "EARC/ARC",
.id = 0,
.playback = {
.channels_min = 1,
.channels_max = 32,
.rates = EARC_RATES,
.formats = EARC_FORMATS,
},
.capture = {
.channels_min = 1,
.channels_max = 32,
.rates = EARC_RATES,
.formats = EARC_FORMATS,
},
.ops = &earc_dai_ops,
},
};
static const char *const attended_type[] = {
"DISCONNECT",
"ARC",
"eARC"
};
static int ss_prepare(struct snd_pcm_substream *substream,
void *pfrddr,
int samesource_sel,
int lane_i2s,
enum aud_codec_types type,
int share_lvl,
int separated)
{
if (s_earc->earctx_on) {
pr_debug("%s earctx priority\n", __func__);
return 0;
}
pr_debug("%s() %d, lvl %d\n", __func__, __LINE__, share_lvl);
sharebuffer_prepare(substream,
pfrddr,
samesource_sel,
lane_i2s,
type,
share_lvl,
separated);
return 0;
}
static int ss_trigger(int cmd, int samesource_sel, bool reenable)
{
if (s_earc->earctx_on) {
pr_debug("%s earctx priority\n", __func__);
return 0;
}
pr_debug("%s() ss %d\n", __func__, samesource_sel);
sharebuffer_trigger(cmd, samesource_sel, reenable);
return 0;
}
static int ss_free(struct snd_pcm_substream *substream,
void *pfrddr, int samesource_sel, int share_lvl)
{
if (s_earc->earctx_on) {
pr_debug("%s earctx priority\n", __func__);
return 0;
}
pr_info("%s() samesrc %d, lvl %d\n",
__func__, samesource_sel, share_lvl);
if (aml_check_sharebuffer_valid(pfrddr,
samesource_sel)) {
sharebuffer_free(substream,
pfrddr, samesource_sel, share_lvl);
}
return 0;
}
struct samesrc_ops earctx_ss_ops = {
.prepare = ss_prepare,
.trigger = ss_trigger,
.hw_free = ss_free,
};
const struct soc_enum attended_type_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(attended_type),
attended_type);
static int earcrx_get_attend_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum attend_type type;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
type = earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map);
ucontrol->value.integer.value[0] = type;
return 0;
}
static int earcrx_set_attend_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum cmdc_st state;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
state = earcrx_cmdc_get_state(p_earc->rx_cmdc_map);
if (state != CMDC_ST_IDLE2)
return 0;
/* only support set cmdc from idle to ARC */
return 0;
}
static int earcrx_arc_get_enable(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum attend_type type;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
type = earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map);
ucontrol->value.integer.value[0] = (bool)(type == ATNDTYP_ARC);
return 0;
}
static int earcrx_arc_set_enable(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
earcrx_cmdc_arc_connect(p_earc->rx_cmdc_map,
(bool)ucontrol->value.integer.value[0]);
return 0;
}
static int earctx_get_attend_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum attend_type type;
if (!p_earc || IS_ERR(p_earc->tx_cmdc_map))
return 0;
type = earctx_cmdc_get_attended_type(p_earc->tx_cmdc_map);
ucontrol->value.integer.value[0] = type;
return 0;
}
static int earctx_set_attend_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum cmdc_st state;
if (!p_earc || IS_ERR(p_earc->tx_cmdc_map))
return 0;
state = earctx_cmdc_get_state(p_earc->tx_cmdc_map);
if (state != CMDC_ST_IDLE2)
return 0;
/* only support set cmdc from idle to ARC */
return 0;
}
static int earcrx_get_latency(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum cmdc_st state;
u8 val = 0;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
state = earcrx_cmdc_get_state(p_earc->rx_cmdc_map);
if (state != CMDC_ST_EARC)
return 0;
earcrx_cmdc_get_latency(p_earc->rx_cmdc_map, &val);
ucontrol->value.integer.value[0] = val;
return 0;
}
static int earcrx_set_latency(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
u8 latency = ucontrol->value.integer.value[0];
enum cmdc_st state;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
state = earcrx_cmdc_get_state(p_earc->rx_cmdc_map);
if (state != CMDC_ST_EARC)
return 0;
earcrx_cmdc_set_latency(p_earc->rx_cmdc_map, &latency);
return 0;
}
static int earcrx_get_cds(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kcontrol->private_value;
u8 *value = (u8 *)ucontrol->value.bytes.data;
int i;
if (!p_earc || IS_ERR(p_earc->rx_top_map))
return 0;
for (i = 0; i < bytes_ext->max; i++)
*value++ = p_earc->cds_data[i];
return 0;
}
static int earcrx_set_cds(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum cmdc_st state;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
memcpy(p_earc->cds_data, ucontrol->value.bytes.data, CDS_MAX_BYTES);
state = earcrx_cmdc_get_state(p_earc->rx_cmdc_map);
if (state == CMDC_ST_EARC)
earcrx_cmdc_set_cds(p_earc->rx_cmdc_map, p_earc->cds_data);
return 0;
}
int earcrx_get_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
unsigned long flags;
int mute = 0;
if (!p_earc || IS_ERR(p_earc->rx_dmac_map))
return 0;
spin_lock_irqsave(&p_earc->rx_lock, flags);
if (p_earc->rx_dmac_clk_on)
mute = earcrx_get_cs_mute(p_earc->rx_dmac_map);
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
ucontrol->value.integer.value[0] = mute;
return 0;
}
int earcrx_get_audio_coding_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum audio_coding_types coding_type;
enum attend_type type;
unsigned long flags;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
if (!p_earc->rx_dmac_clk_on)
return 0;
type = earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map);
spin_lock_irqsave(&p_earc->rx_lock, flags);
coding_type = earcrx_get_cs_fmt(p_earc->rx_dmac_map, type);
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
ucontrol->value.integer.value[0] = coding_type;
return 0;
}
int earcrx_get_freq(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum audio_coding_types coding_type = 0;
enum attend_type type;
int freq = 0;
unsigned long flags;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map))
return 0;
if (!p_earc->rx_dmac_clk_on)
return 0;
type = earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map);
spin_lock_irqsave(&p_earc->rx_lock, flags);
if (p_earc->rx_dmac_clk_on) {
coding_type = earcrx_get_cs_fmt(p_earc->rx_dmac_map, type);
freq = earcrx_get_cs_freq(p_earc->rx_dmac_map, coding_type);
}
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
ucontrol->value.integer.value[0] = freq;
return 0;
}
int earcrx_get_word_length(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
unsigned long flags;
int wlen = 0;
if (!p_earc || IS_ERR(p_earc->rx_dmac_map))
return 0;
spin_lock_irqsave(&p_earc->rx_lock, flags);
if (p_earc->rx_dmac_clk_on)
wlen = earcrx_get_cs_word_length(p_earc->rx_dmac_map);
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
ucontrol->value.integer.value[0] = wlen;
return 0;
}
static int earctx_get_latency(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum cmdc_st state;
u8 val = 0;
if (!p_earc || IS_ERR(p_earc->tx_cmdc_map))
return 0;
state = earctx_cmdc_get_state(p_earc->tx_cmdc_map);
if (state != CMDC_ST_EARC)
return 0;
earctx_cmdc_get_latency(p_earc->tx_cmdc_map, &val);
ucontrol->value.integer.value[0] = val;
return 0;
}
static int earctx_set_latency(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
u8 latency = ucontrol->value.integer.value[0];
enum cmdc_st state;
if (!p_earc || IS_ERR(p_earc->tx_cmdc_map))
return 0;
state = earctx_cmdc_get_state(p_earc->tx_cmdc_map);
if (state != CMDC_ST_EARC)
return 0;
earctx_cmdc_set_latency(p_earc->tx_cmdc_map, &latency);
return 0;
}
static int earctx_get_cds(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kcontrol->private_value;
u8 *value = (u8 *)ucontrol->value.bytes.data;
enum cmdc_st state;
u8 data[256];
int i;
if (!p_earc || IS_ERR(p_earc->tx_cmdc_map))
return 0;
state = earctx_cmdc_get_state(p_earc->tx_cmdc_map);
if (state != CMDC_ST_EARC)
return 0;
earctx_cmdc_get_cds(p_earc->tx_cmdc_map, data);
for (i = 0; i < bytes_ext->max; i++)
*value++ = data[i];
return 0;
}
int earctx_get_audio_coding_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
if (!p_earc || IS_ERR(p_earc->tx_top_map))
return 0;
ucontrol->value.integer.value[0] = p_earc->tx_audio_coding_type;
return 0;
}
int earctx_set_audio_coding_type(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum audio_coding_types new_coding_type = ucontrol->value.integer.value[0];
if (!p_earc || IS_ERR(p_earc->tx_cmdc_map) ||
new_coding_type == p_earc->tx_audio_coding_type)
return 0;
return aml_earctx_set_audio_coding_type(new_coding_type);
}
int earctx_get_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
if (!p_earc || IS_ERR(p_earc->tx_top_map))
return 0;
if (!p_earc->tx_dmac_clk_on)
return 0;
ucontrol->value.integer.value[0] = p_earc->tx_cs_mute;
return 0;
}
/*
* eARC TX asserts the Channel Status Mute bit
* to eARC RX before format change
*/
int earctx_set_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
bool mute = ucontrol->value.integer.value[0];
if (!p_earc || IS_ERR(p_earc->tx_dmac_map))
return 0;
p_earc->tx_cs_mute = mute;
if (!p_earc->tx_dmac_clk_on)
return 0;
earctx_set_cs_mute(p_earc->tx_dmac_map, mute);
return 0;
}
static void earctx_set_earc_mode(struct earc *p_earc, bool earc_mode)
{
/* set arc initiated and arc_enable */
earctx_cmdc_arc_connect(p_earc->tx_cmdc_map, !earc_mode);
/* set earc mode */
earctx_cmdc_earc_mode(p_earc->tx_cmdc_map, earc_mode);
#if (defined CONFIG_AMLOGIC_MEDIA_TVIN_HDMI ||\
defined CONFIG_AMLOGIC_MEDIA_TVIN_HDMI_MODULE)
if (p_earc->tx_earc_mode) {
p_earc->tx_reset_hpd = true;
rx_earc_hpd_cntl(); /* reset hpd */
}
#endif
}
static void tx_resume_work_func(struct work_struct *p_work)
{
struct earc *p_earc = container_of(p_work, struct earc, tx_resume_work);
msleep(2500);
pr_info("%s reset hpd\n", __func__);
earctx_set_earc_mode(p_earc, true);
}
int earctx_earc_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
int earc_mode = ucontrol->value.integer.value[0];
if (!p_earc || IS_ERR(p_earc->tx_cmdc_map) || p_earc->tx_earc_mode == earc_mode)
return 0;
p_earc->tx_earc_mode = earc_mode;
earctx_set_earc_mode(p_earc, earc_mode);
return 0;
}
int earctx_earc_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
if (!p_earc)
return 0;
ucontrol->value.integer.value[0] = p_earc->tx_earc_mode;
return 0;
}
static int earcrx_get_iec958(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
unsigned long flags;
int cs = 0;
if (!p_earc || IS_ERR(p_earc->rx_dmac_map))
return 0;
spin_lock_irqsave(&p_earc->rx_lock, flags);
if (p_earc->rx_dmac_clk_on)
cs = earcrx_get_cs_iec958(p_earc->rx_dmac_map);
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
ucontrol->value.iec958.status[0] = (cs >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (cs >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (cs >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (cs >> 24) & 0xff;
dev_info(p_earc->dev,
"x get[AES0=%#x AES1=%#x AES2=%#x AES3=%#x]\n",
ucontrol->value.iec958.status[0],
ucontrol->value.iec958.status[1],
ucontrol->value.iec958.status[2],
ucontrol->value.iec958.status[3]
);
return 0;
}
static int earctx_get_iec958(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
int cs;
if (!p_earc || IS_ERR(p_earc->tx_dmac_map))
return 0;
if (!p_earc->tx_dmac_clk_on)
return 0;
cs = earctx_get_cs_iec958(p_earc->tx_dmac_map);
ucontrol->value.iec958.status[0] = (cs >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (cs >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (cs >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (cs >> 24) & 0xff;
dev_info(p_earc->dev,
"x get[AES0=%#x AES1=%#x AES2=%#x AES3=%#x]\n",
ucontrol->value.iec958.status[0],
ucontrol->value.iec958.status[1],
ucontrol->value.iec958.status[2],
ucontrol->value.iec958.status[3]
);
return 0;
}
static int earctx_set_iec958(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
int cs = 0;
if (!p_earc || IS_ERR(p_earc->tx_dmac_map))
return 0;
if (!p_earc->tx_dmac_clk_on)
return 0;
cs |= ucontrol->value.iec958.status[0];
cs |= ucontrol->value.iec958.status[1] << 8;
cs |= ucontrol->value.iec958.status[2] << 16;
cs |= ucontrol->value.iec958.status[3] << 24;
earctx_set_cs_iec958(p_earc->tx_dmac_map, cs);
dev_info(p_earc->dev,
"x put [AES0=%#x AES1=%#x AES2=%#x AES3=%#x]\n",
ucontrol->value.iec958.status[0],
ucontrol->value.iec958.status[1],
ucontrol->value.iec958.status[2],
ucontrol->value.iec958.status[3]
);
return 0;
}
static const struct snd_pcm_chmap_elem snd_pcm_chmaps[] = {
{ .channels = 2,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR }
},
{ .channels = 3,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
SNDRV_CHMAP_LFE }
},
{ .channels = 6,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE }
},
{ .channels = 8,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC }
}
};
static bool support_chmap(enum audio_coding_types coding_type)
{
if (coding_type == AUDIO_CODING_TYPE_STEREO_LPCM ||
coding_type == AUDIO_CODING_TYPE_MULTICH_2CH_LPCM ||
coding_type == AUDIO_CODING_TYPE_MULTICH_8CH_LPCM)
return true;
return false;
}
static int earcrx_get_ca(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
enum audio_coding_types coding_type;
unsigned long flags;
if (!p_earc || IS_ERR(p_earc->rx_cmdc_map)) {
ucontrol->value.integer.value[0] = 0xff;
return 0;
}
if (!p_earc->rx_dmac_clk_on) {
ucontrol->value.integer.value[0] = 0xff;
return 0;
}
spin_lock_irqsave(&p_earc->rx_lock, flags);
if (p_earc->rx_dmac_clk_on) {
coding_type = earcrx_get_cs_fmt(p_earc->rx_dmac_map,
earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map));
ucontrol->value.integer.value[0] =
earcrx_get_cs_ca(p_earc->rx_dmac_map);
} else {
ucontrol->value.integer.value[0] = 0xff;
}
spin_unlock_irqrestore(&p_earc->rx_lock, flags);
return 0;
}
static int earctx_get_ca(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
if (!p_earc || IS_ERR(p_earc->tx_top_map)) {
ucontrol->value.integer.value[0] = 0xff;
return 0;
}
if (support_chmap(p_earc->tx_audio_coding_type))
ucontrol->value.integer.value[0] = p_earc->tx_cs_lpcm_ca;
else
ucontrol->value.integer.value[0] = 0xff;
return 0;
}
static int earctx_set_ca(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct earc *p_earc = dev_get_drvdata(component->dev);
int ca = ucontrol->value.integer.value[0];
if (!p_earc || IS_ERR(p_earc->tx_top_map))
return 0;
if (ca >= 0x32)
return -EINVAL;
p_earc->tx_cs_lpcm_ca = ca;
if (!p_earc->tx_dmac_clk_on)
return 0;
if (!support_chmap(p_earc->tx_audio_coding_type))
ca = 0;
/* runtime, update to channel status */
earctx_set_cs_ca(p_earc->tx_dmac_map, ca);
return 0;
}
static const struct snd_kcontrol_new earc_controls[] = {
SOC_SINGLE_BOOL_EXT("eARC ARC Switch",
0,
earcrx_arc_get_enable,
earcrx_arc_set_enable),
SOC_ENUM_EXT("eARC_RX attended type",
attended_type_enum,
earcrx_get_attend_type,
earcrx_set_attend_type),
SOC_ENUM_EXT("eARC_TX attended type",
attended_type_enum,
earctx_get_attend_type,
earctx_set_attend_type),
SND_SOC_BYTES_EXT("eARC_RX Latency",
1,
earcrx_get_latency,
earcrx_set_latency),
SND_SOC_BYTES_EXT("eARC_TX Latency",
1,
earctx_get_latency,
earctx_set_latency),
SND_SOC_BYTES_EXT("eARC_RX CDS",
CDS_MAX_BYTES,
earcrx_get_cds,
earcrx_set_cds),
SND_SOC_BYTES_EXT("eARC_TX CDS",
CDS_MAX_BYTES,
earctx_get_cds,
NULL),
SOC_ENUM_EXT("eARC_RX Audio Coding Type",
audio_coding_type_enum,
earcrx_get_audio_coding_type,
NULL),
SOC_ENUM_EXT("eARC_TX Audio Coding Type",
audio_coding_type_enum,
earctx_get_audio_coding_type,
earctx_set_audio_coding_type),
SND_SOC_BYTES_EXT("eARC_RX Channel Allocation",
1,
earcrx_get_ca,
NULL),
SND_SOC_BYTES_EXT("eARC_TX Channel Allocation",
1,
earctx_get_ca,
earctx_set_ca),
SOC_SINGLE_BOOL_EXT("eARC_RX CS Mute",
0,
earcrx_get_mute,
NULL),
SOC_SINGLE_BOOL_EXT("eARC_TX CS Mute",
0,
earctx_get_mute,
earctx_set_mute),
SOC_SINGLE_BOOL_EXT("eARC_TX eARC Mode",
0,
earctx_earc_mode_get,
earctx_earc_mode_put),
SOC_SINGLE_EXT("eARC_RX Audio Sample Frequency",
0, 0, 384000, 0,
earcrx_get_freq,
NULL),
SOC_SINGLE_EXT("eARC_RX Audio Word Length",
0, 0, 32, 0,
earcrx_get_word_length,
NULL),
/* Status cchanel controller */
SND_IEC958(SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
earcrx_get_iec958,
NULL),
SND_IEC958(SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
earctx_get_iec958,
earctx_set_iec958),
};
static const struct snd_soc_component_driver earc_component = {
.controls = earc_controls,
.ops = &earc_ops,
.num_controls = ARRAY_SIZE(earc_controls),
.name = DRV_NAME,
};
struct earc_chipinfo sm1_earc_chipinfo = {
.earc_spdifout_lane_mask = EARC_SPDIFOUT_LANE_MASK_V1,
.rx_dmac_sync_int = false,
.rx_enable = true,
.tx_enable = false,
};
struct earc_chipinfo tm2_earc_chipinfo = {
.earc_spdifout_lane_mask = EARC_SPDIFOUT_LANE_MASK_V1,
.rx_dmac_sync_int = false,
.rx_enable = true,
.tx_enable = true,
};
struct earc_chipinfo tm2_revb_earc_chipinfo = {
.earc_spdifout_lane_mask = EARC_SPDIFOUT_LANE_MASK_V2,
.rx_dmac_sync_int = true,
.rx_enable = true,
.tx_enable = true,
};
struct earc_chipinfo t7_earc_chipinfo = {
.earc_spdifout_lane_mask = EARC_SPDIFOUT_LANE_MASK_V2,
.rx_dmac_sync_int = false,
.rterm_on = true,
.no_ana_auto_cal = true,
.chnum_mult_mode = true,
.unstable_tick_sel = true,
.rx_enable = true,
.tx_enable = true,
};
struct earc_chipinfo t3_earc_chipinfo = {
.earc_spdifout_lane_mask = EARC_SPDIFOUT_LANE_MASK_V2,
.rx_dmac_sync_int = false,
.rterm_on = true,
.no_ana_auto_cal = true,
.chnum_mult_mode = true,
.unstable_tick_sel = true,
.rx_enable = false,
.tx_enable = true,
};
static const struct of_device_id earc_device_id[] = {
{
.compatible = "amlogic, sm1-snd-earc",
.data = &sm1_earc_chipinfo,
},
{
.compatible = "amlogic, tm2-snd-earc",
.data = &tm2_earc_chipinfo,
},
{
.compatible = "amlogic, tm2-revb-snd-earc",
.data = &tm2_revb_earc_chipinfo,
},
{
.compatible = "amlogic, t7-snd-earc",
.data = &t7_earc_chipinfo,
},
{
.compatible = "amlogic, t3-snd-earc",
.data = &t3_earc_chipinfo,
},
{}
};
MODULE_DEVICE_TABLE(of, earc_device_id);
static const unsigned int earcrx_extcon[] = {
EXTCON_EARCRX_ATNDTYP_ARC,
EXTCON_EARCRX_ATNDTYP_EARC,
EXTCON_NONE,
};
static int earcrx_extcon_register(struct earc *p_earc)
{
int ret = 0;
/* earc or arc connect */
p_earc->rx_edev = devm_extcon_dev_allocate(p_earc->dev, earcrx_extcon);
if (IS_ERR(p_earc->rx_edev)) {
dev_err(p_earc->dev, "failed to allocate earc extcon!!!\n");
ret = -ENOMEM;
return ret;
}
/*
* p_earc->rx_edev->dev.parent = p_earc->dev;
* p_earc->rx_edev->name = "earcrx";
* dev_set_name(&p_earc->rx_edev->dev, "earcrx");
*/
ret = devm_extcon_dev_register(p_earc->dev, p_earc->rx_edev);
if (ret < 0) {
dev_err(p_earc->dev, "earc extcon failed to register!!\n");
return ret;
}
return ret;
}
void earc_hdmitx_hpdst(bool st)
{
struct earc *p_earc = s_earc;
if (!p_earc)
return;
dev_info(p_earc->dev, "HDMITX cable is %s\n",
st ? "plugin" : "plugout");
if (!p_earc->rx_bootup_auto_cal) {
p_earc->rx_bootup_auto_cal = true;
p_earc->event |= EVENT_RX_ANA_AUTO_CAL;
schedule_work(&p_earc->work);
}
/* rx cmdc init */
earcrx_cmdc_init(p_earc->rx_top_map,
st,
p_earc->chipinfo->rx_dmac_sync_int,
p_earc->chipinfo->rterm_on);
if (st)
earcrx_cmdc_int_mask(p_earc->rx_top_map);
earcrx_cmdc_arc_connect(p_earc->rx_cmdc_map, st);
earcrx_cmdc_hpd_detect(p_earc->rx_cmdc_map, st);
}
static int earcrx_cmdc_setup(struct earc *p_earc)
{
int ret = 0;
/* set cmdc clk */
audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 6, 0x1 << 6);
if (!IS_ERR(p_earc->clk_rx_cmdc)) {
clk_set_rate(p_earc->clk_rx_cmdc, 10000000);
ret = clk_prepare_enable(p_earc->clk_rx_cmdc);
if (ret) {
dev_err(p_earc->dev, "Can't enable earc clk_rx_cmdc\n");
return ret;
}
}
ret = devm_request_threaded_irq(p_earc->dev,
p_earc->irq_earc_rx,
rx_handler,
earc_rx_isr,
IRQF_TRIGGER_HIGH |
IRQF_ONESHOT,
"earc_rx",
p_earc);
if (ret) {
dev_err(p_earc->dev, "failed to claim earc_rx %u\n",
p_earc->irq_earc_rx);
return ret;
}
/* Default: arc arc_initiated */
earcrx_cmdc_arc_connect(p_earc->rx_cmdc_map, true);
return ret;
}
void earc_hdmirx_hpdst(int earc_port, bool st)
{
struct earc *p_earc = s_earc;
if (!p_earc)
return;
dev_info(p_earc->dev, "HDMIRX cable port:%d is %s\n",
earc_port,
st ? "plugin" : "plugout");
if (!st) {
/* set discnnect when cable plugout */
p_earc->earctx_connected_device_type = ATNDTYP_DISCNCT;
/* release earctx same source when cable plug out */
aml_check_and_release_sharebuffer(NULL, EARCTX_DMAC);
/* disable arc */
earctx_cmdc_arc_connect(p_earc->tx_cmdc_map, st);
} else {
/* set ARC type as defaule when cable plugin */
p_earc->earctx_connected_device_type = ATNDTYP_ARC;
}
if (!p_earc->tx_bootup_auto_cal) {
p_earc->tx_bootup_auto_cal = true;
p_earc->event |= EVENT_TX_ANA_AUTO_CAL;
schedule_work(&p_earc->work);
}
/* tx cmdc anlog init */
earctx_cmdc_init(p_earc->tx_top_map, st, p_earc->chipinfo->rterm_on);
earctx_cmdc_earc_mode(p_earc->tx_cmdc_map, p_earc->tx_earc_mode);
earctx_cmdc_hpd_detect(p_earc->tx_top_map,
p_earc->tx_cmdc_map,
earc_port, st);
}
static int earctx_cmdc_setup(struct earc *p_earc)
{
int ret = 0;
/* set cmdc clk */
audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 5, 0x1 << 5);
if (!IS_ERR(p_earc->clk_tx_cmdc)) {
clk_set_rate(p_earc->clk_tx_cmdc, 10000000);
ret = clk_prepare_enable(p_earc->clk_tx_cmdc);
if (ret) {
dev_err(p_earc->dev, "Can't enable earc clk_tx_cmdc\n");
return ret;
}
}
ret = devm_request_threaded_irq(p_earc->dev,
p_earc->irq_earc_tx,
NULL,
earc_tx_isr,
IRQF_TRIGGER_HIGH |
IRQF_ONESHOT,
"earc_tx",
p_earc);
if (ret) {
dev_err(p_earc->dev, "failed to claim earc_tx %u\n",
p_earc->irq_earc_tx);
return ret;
}
/* Default: no time out to connect RX */
earctx_cmdc_set_timeout(p_earc->tx_cmdc_map, 1);
/* Default: arc arc_initiated */
earctx_cmdc_int_mask(p_earc->tx_top_map);
return ret;
}
static void earc_work_func(struct work_struct *work)
{
struct earc *p_earc = container_of(work, struct earc, work);
/* RX */
if (!IS_ERR(p_earc->rx_top_map) &&
!p_earc->chipinfo->no_ana_auto_cal &&
p_earc->event & EVENT_RX_ANA_AUTO_CAL) {
p_earc->event &= ~EVENT_RX_ANA_AUTO_CAL;
earcrx_ana_auto_cal(p_earc->rx_top_map);
}
/* TX */
if (!IS_ERR(p_earc->tx_top_map) &&
!p_earc->chipinfo->no_ana_auto_cal &&
p_earc->event & EVENT_TX_ANA_AUTO_CAL) {
p_earc->event &= ~EVENT_TX_ANA_AUTO_CAL;
earctx_ana_auto_cal(p_earc->tx_top_map);
}
}
static int earc_platform_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *node_prt = NULL;
struct platform_device *pdev_parent;
struct device *dev = &pdev->dev;
struct aml_audio_controller *actrl = NULL;
struct earc *p_earc = NULL;
int ret = 0;
int ss = 0;
p_earc = devm_kzalloc(dev, sizeof(struct earc), GFP_KERNEL);
if (!p_earc)
return -ENOMEM;
ret = of_property_read_u32(dev->of_node, "samesource_sel", &ss);
if (ret < 0)
p_earc->samesource_sel = SHAREBUFFER_NONE;
else
p_earc->samesource_sel = ss;
p_earc->dev = dev;
dev_set_drvdata(dev, p_earc);
p_earc->chipinfo = (struct earc_chipinfo *)
of_device_get_match_data(dev);
if (!p_earc->chipinfo) {
dev_warn_once(dev, "check whether to update earc chipinfo\n");
return -EINVAL;
}
/* get audio controller */
node_prt = of_get_parent(node);
if (!node_prt)
return -ENXIO;
pdev_parent = of_find_device_by_node(node_prt);
of_node_put(node_prt);
actrl = (struct aml_audio_controller *)
platform_get_drvdata(pdev_parent);
p_earc->actrl = actrl;
p_earc->tx_cmdc_map = regmap_resource(dev, "tx_cmdc");
if (!p_earc->tx_cmdc_map)
dev_info(dev, "Can't get earctx_cmdc regmap!!\n");
p_earc->tx_dmac_map = regmap_resource(dev, "tx_dmac");
if (!p_earc->tx_dmac_map)
dev_info(dev, "Can't get earctx_dmac regmap!!\n");
p_earc->tx_top_map = regmap_resource(dev, "tx_top");
if (!p_earc->tx_top_map)
dev_info(dev, "Can't get earctx_top regmap!!\n");
p_earc->rx_cmdc_map = regmap_resource(dev, "rx_cmdc");
if (!p_earc->rx_cmdc_map)
dev_info(dev, "Can't get earcrx_cdmc regmap!!\n");
p_earc->rx_dmac_map = regmap_resource(dev, "rx_dmac");
if (!p_earc->rx_dmac_map)
dev_info(dev, "Can't get earcrx_dmac regmap!!\n");
p_earc->rx_top_map = regmap_resource(dev, "rx_top");
if (!p_earc->rx_top_map)
dev_info(dev, "Can't get earcrx_top regmap!!\n");
/* RX */
if (p_earc->chipinfo->rx_enable) {
spin_lock_init(&p_earc->rx_lock);
p_earc->clk_rx_cmdc = devm_clk_get(&pdev->dev, "rx_cmdc");
if (IS_ERR(p_earc->clk_rx_cmdc))
dev_info(&pdev->dev, "Can't get clk_rx_cmdc\n");
p_earc->clk_rx_dmac = devm_clk_get(&pdev->dev, "rx_dmac");
if (IS_ERR(p_earc->clk_rx_dmac))
dev_info(&pdev->dev, "Can't get clk_rx_dmac\n");
p_earc->clk_rx_cmdc_srcpll = devm_clk_get(&pdev->dev, "rx_cmdc_srcpll");
if (IS_ERR(p_earc->clk_rx_cmdc_srcpll))
dev_info(&pdev->dev, "Can't get clk_rx_cmdc_srcpll\n");
p_earc->clk_rx_dmac_srcpll = devm_clk_get(&pdev->dev, "rx_dmac_srcpll");
if (IS_ERR(p_earc->clk_rx_dmac_srcpll))
dev_info(&pdev->dev, "Can't get clk_rx_dmac_srcpll\n");
if (!IS_ERR(p_earc->clk_rx_cmdc) && !IS_ERR(p_earc->clk_rx_cmdc_srcpll)) {
ret = clk_set_parent(p_earc->clk_rx_cmdc, p_earc->clk_rx_cmdc_srcpll);
if (ret) {
dev_err(dev, "Can't set clk_rx_cmdc parent clock\n");
return ret;
}
}
if (!IS_ERR(p_earc->clk_rx_dmac) && !IS_ERR(p_earc->clk_rx_dmac_srcpll)) {
ret = clk_set_parent(p_earc->clk_rx_dmac, p_earc->clk_rx_dmac_srcpll);
if (ret) {
dev_err(dev, "Can't set clk_rx_dmac parent clock\n");
return ret;
}
}
p_earc->irq_earc_rx =
platform_get_irq_byname(pdev, "earc_rx");
if (p_earc->irq_earc_rx < 0)
dev_err(dev, "platform get irq earc_rx failed\n");
else
dev_info(dev, "%s, irq_earc_rx:%d\n", __func__, p_earc->irq_earc_rx);
}
/* TX */
if (p_earc->chipinfo->tx_enable) {
p_earc->clk_tx_cmdc = devm_clk_get(&pdev->dev, "tx_cmdc");
if (IS_ERR(p_earc->clk_tx_cmdc))
dev_info(&pdev->dev, "Check whether support eARC TX\n");
p_earc->clk_tx_dmac = devm_clk_get(&pdev->dev, "tx_dmac");
if (IS_ERR(p_earc->clk_tx_dmac))
dev_info(&pdev->dev, "Check whether support eARC TX\n");
p_earc->clk_tx_cmdc_srcpll = devm_clk_get(&pdev->dev, "tx_cmdc_srcpll");
if (IS_ERR(p_earc->clk_tx_cmdc_srcpll))
dev_info(&pdev->dev, "Check whether support eARC TX\n");
p_earc->clk_tx_dmac_srcpll = devm_clk_get(&pdev->dev, "tx_dmac_srcpll");
if (IS_ERR(p_earc->clk_tx_dmac_srcpll))
dev_info(&pdev->dev, "Check whether support eARC TX\n");
if (!IS_ERR(p_earc->clk_tx_cmdc) && !IS_ERR(p_earc->clk_tx_cmdc_srcpll)) {
ret = clk_set_parent(p_earc->clk_tx_cmdc, p_earc->clk_tx_cmdc_srcpll);
if (ret) {
dev_err(dev, "Can't set clk_tx_cmdc parent clock\n");
return ret;
}
}
if (!IS_ERR(p_earc->clk_tx_dmac) && !IS_ERR(p_earc->clk_tx_dmac_srcpll)) {
ret = clk_set_parent(p_earc->clk_tx_dmac, p_earc->clk_tx_dmac_srcpll);
if (ret) {
dev_err(dev, "Can't set clk_tx_dmac parent clock\n");
return ret;
}
}
p_earc->irq_earc_tx =
platform_get_irq_byname(pdev, "earc_tx");
if (p_earc->irq_earc_tx < 0)
dev_err(dev, "platform get irq earc_tx failed\n");
else
dev_info(dev, "%s, irq_earc_tx:%d\n", __func__, p_earc->irq_earc_tx);
}
ret = snd_soc_register_component(&pdev->dev,
&earc_component,
earc_dai,
ARRAY_SIZE(earc_dai));
if (ret) {
dev_err(&pdev->dev,
"snd_soc_register_component failed\n");
return ret;
}
p_earc->tx_earc_mode = true;
s_earc = p_earc;
/* RX */
if (!IS_ERR(p_earc->rx_top_map)) {
earcrx_extcon_register(p_earc);
earcrx_cmdc_setup(p_earc);
}
/* TX */
if (!IS_ERR(p_earc->tx_top_map)) {
earctx_cmdc_setup(p_earc);
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_HDMI
register_earctx_callback(earc_hdmirx_hpdst);
#endif
earctx_ss_ops.private = p_earc;
register_samesrc_ops(SHAREBUFFER_EARCTX, &earctx_ss_ops);
INIT_WORK(&p_earc->tx_resume_work, tx_resume_work_func);
}
if ((!IS_ERR(p_earc->rx_top_map)) ||
(!IS_ERR(p_earc->tx_top_map))) {
INIT_WORK(&p_earc->work, earc_work_func);
INIT_WORK(&p_earc->rx_dmac_int_work, valid_auto_work_func);
}
if (!IS_ERR(p_earc->rx_top_map))
register_earcrx_callback(earc_hdmitx_hpdst);
dev_err(dev, "registered eARC platform\n");
return 0;
}
int earc_platform_remove(struct platform_device *pdev)
{
if (!IS_ERR(s_earc->rx_top_map))
unregister_earcrx_callback();
#ifdef CONFIG_AMLOGIC_MEDIA_TVIN_HDMI
if (!IS_ERR(s_earc->tx_top_map))
unregister_earctx_callback();
#endif
s_earc = NULL;
snd_soc_unregister_component(&pdev->dev);
return 0;
}
static int earc_platform_resume(struct platform_device *pdev)
{
struct earc *p_earc = dev_get_drvdata(&pdev->dev);
/* earc device, and the state is arc, need recovery earc */
if (!IS_ERR(p_earc->tx_cmdc_map) &&
p_earc->tx_earc_mode &&
p_earc->earctx_connected_device_type == ATNDTYP_EARC &&
earctx_cmdc_get_attended_type(p_earc->tx_cmdc_map) == ATNDTYP_ARC)
schedule_work(&p_earc->tx_resume_work);
return 0;
}
struct platform_driver earc_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = earc_device_id,
},
.probe = earc_platform_probe,
.remove = earc_platform_remove,
.resume = earc_platform_resume,
};
int __init earc_init(void)
{
return platform_driver_register(&earc_driver);
}
void __exit earc_exit(void)
{
platform_driver_unregister(&earc_driver);
}
#ifndef MODULE
arch_initcall_sync(earc_init);
module_exit(earc_exit);
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("Amlogic eARC/ARC TX/RX ASoc driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("Platform:" DRV_NAME);
MODULE_DEVICE_TABLE(of, earc_device_id);
#endif