blob: 87a90206eb7a42a633e236a6fed58b7bf5dc096d [file] [log] [blame]
/*
* sound/soc/amlogic/auge/earc.c
*
* Copyright (C) 2019 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.
*
* Audio External Input/Out drirver
* such as fratv, frhdmirx
*/
#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.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 <linux/amlogic/media/sound/hdmi_earc.h>
#include <linux/amlogic/media/sound/mixer.h>
#include "ddr_mngr.h"
#include "earc_hw.h"
#define DRV_NAME "EARC"
struct earc {
struct aml_audio_controller *actrl;
struct device *dev;
struct clk *clk_rx_gate;
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;
struct extcon_dev *tx_edev;
bool rx_dmac_clk_on;
bool tx_dmac_clk_on;
};
static struct earc *s_earc;
#define PREALLOC_BUFFER_MAX (256 * 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 = 256 * 1024,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
.channels_max = 32,
};
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 irqreturn_t earc_rx_isr(int irq, void *data)
{
struct earc *p_earc = (struct earc *)data;
unsigned int status0 = earcrx_cdmc_get_irqs(p_earc->rx_top_map);
if (status0)
earcrx_cdmc_clr_irqs(p_earc->rx_top_map, status0);
if (status0 & INT_EARCRX_CMDC_TIMEOUT) {
earcrx_update_attend_event(p_earc,
false, false);
pr_debug("%s EARCRX_CMDC_TIMEOUT\n", __func__);
}
if (status0 & INT_EARCRX_CMDC_IDLE2) {
earcrx_update_attend_event(p_earc,
false, true);
pr_info("%s EARCRX_CMDC_IDLE2\n", __func__);
}
if (status0 & INT_EARCRX_CMDC_IDLE1) {
earcrx_update_attend_event(p_earc,
false, false);
pr_info("%s EARCRX_CMDC_IDLE1\n", __func__);
}
if (status0 & INT_EARCRX_CMDC_DISC2)
pr_debug("%s EARCRX_CMDC_DISC2\n", __func__);
if (status0 & INT_EARCRX_CMDC_DISC1)
pr_debug("%s EARCRX_CMDC_DISC1\n", __func__);
if (status0 & INT_EARCRX_CMDC_EARC) {
earcrx_update_attend_event(p_earc,
true, true);
pr_info("%s EARCRX_CMDC_EARC\n", __func__);
}
/*
* if (status0 & INT_EARCRX_CMDC_HB_STATUS)
* pr_debug("%s EARCRX_CMDC_HB_STATUS\n", __func__);
*/
if (status0 & INT_EARCRX_CMDC_LOSTHB)
pr_debug("%s EARCRX_CMDC_LOSTHB\n", __func__);
if (p_earc->rx_dmac_clk_on) {
unsigned int status1 = earcrx_dmac_get_irqs(p_earc->rx_top_map);
if (status1)
earcrx_dmac_clr_irqs(p_earc->rx_top_map, status1);
if (status1 & INT_ARCRX_BIPHASE_DECODE_C_FIND_PAPB)
pr_debug("%s ARCRX_C_FIND_PAPB\n", __func__);
if (status1 & INT_ARCRX_BIPHASE_DECODE_C_VALID_CHANGE)
pr_debug("%s ARCRX_C_VALID_CHANGE\n", __func__);
if (status1 & INT_ARCRX_BIPHASE_DECODE_C_FIND_NONPCM2PCM)
pr_debug("%s ARCRX_C_FIND_NONPCM2PCM\n", __func__);
if (status1 & INT_ARCRX_BIPHASE_DECODE_C_PCPD_CHANGE)
pr_debug("%s ARCRX_C_PCPD_CHANGE\n", __func__);
if (status1 & INT_ARCRX_BIPHASE_DECODE_C_CH_STATUS_CHANGE)
pr_debug("%s ARCRX_C_CH_STATUS_CHANGE\n", __func__);
if (status1 & INT_ARCRX_BIPHASE_DECODE_I_SAMPLE_MODE_CHANGE)
pr_debug("%s ARCRX_I_SAMPLE_MODE_CHANGE\n", __func__);
if (status1 & INT_ARCRX_BIPHASE_DECODE_R_PARITY_ERR)
pr_debug("%s ARCRX_R_PARITY_ERR\n", __func__);
}
return IRQ_HANDLED;
}
static void earctx_update_attend_event(struct earc *p_earc,
bool is_earc, bool state)
{
if (state) {
if (is_earc) {
extcon_set_state_sync(p_earc->tx_edev,
EXTCON_EARCTX_ATNDTYP_ARC,
false);
extcon_set_state_sync(p_earc->tx_edev,
EXTCON_EARCTX_ATNDTYP_EARC,
state);
} else {
extcon_set_state_sync(p_earc->tx_edev,
EXTCON_EARCTX_ATNDTYP_ARC,
state);
extcon_set_state_sync(p_earc->tx_edev,
EXTCON_EARCTX_ATNDTYP_EARC,
false);
}
} else {
extcon_set_state_sync(p_earc->tx_edev,
EXTCON_EARCTX_ATNDTYP_ARC,
state);
extcon_set_state_sync(p_earc->tx_edev,
EXTCON_EARCTX_ATNDTYP_EARC,
state);
}
}
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);
if (status0)
earctx_cdmc_clr_irqs(p_earc->tx_top_map, status0);
if (status0 & INT_EARCTX_CMDC_IDLE2) {
earctx_update_attend_event(p_earc,
false, true);
pr_debug("%s EARCTX_CMDC_IDLE2\n", __func__);
}
if (status0 & INT_EARCTX_CMDC_IDLE1) {
earctx_update_attend_event(p_earc,
false, false);
pr_debug("%s EARCTX_CMDC_IDLE1\n", __func__);
}
if (status0 & INT_EARCTX_CMDC_DISC2)
pr_debug("%s EARCTX_CMDC_DISC2\n", __func__);
if (status0 & INT_EARCTX_CMDC_DISC1)
pr_debug("%s EARCTX_CMDC_DISC1\n", __func__);
if (status0 & INT_EARCTX_CMDC_EARC) {
earctx_update_attend_event(p_earc,
true, true);
pr_info("%s EARCTX_CMDC_EARC\n", __func__);
}
if (status0 & INT_EARCTX_CMDC_HB_STATUS)
pr_debug("%s EARCTX_CMDC_HB_STATUS\n", __func__);
if (status0 & INT_EARCTX_CMDC_LOSTHB)
pr_debug("%s EARCTX_CMDC_LOSTHB\n", __func__);
if (status0 & INT_EARCTX_CMDC_TIMEOUT) {
earctx_update_attend_event(p_earc,
false, false);
pr_debug("%s EARCTX_CMDC_TIMEOUT\n", __func__);
}
if (status0 & INT_EARCTX_CMDC_STATUS_CH)
pr_debug("%s EARCTX_CMDC_STATUS_CH\n", __func__);
if (status0 & INT_EARCTX_CMDC_RECV_NACK)
pr_debug("%s EARCTX_CMDC_RECV_NACK\n", __func__);
if (status0 & INT_EARCTX_CMDC_RECV_NORSP)
pr_debug("%s EARCTX_CMDC_RECV_NORSP\n", __func__);
if (status0 & INT_EARCTX_CMDC_RECV_UNEXP)
pr_debug("%s EARCTX_CMDC_RECV_UNEXP\n", __func__);
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)
pr_debug("%s EARCTX_FEM_C_HOLD_CLR\n", __func__);
if (status1 & INT_EARCTX_FEM_C_HOLD_START)
pr_debug("%s EARCTX_FEM_C_HOLD_START\n", __func__);
if (status1 & INT_EARCTX_ERRCORR_C_FIFO_THD_LESS_PASS)
pr_debug("%s EARCTX_ERRCORR_C_FIFO_THD_LESS_PASS\n",
__func__);
if (status1 & INT_EARCTX_ERRCORR_C_FIFO_OVERFLOW)
pr_debug("%s EARCTX_ERRCORR_C_FIFO_OVERFLOW\n",
__func__);
if (status1 & INT_EARCTX_ERRCORR_C_FIFO_EMPTY)
pr_debug("%s EARCTX_ERRCORR_C_FIFO_EMPTY\n", __func__);
}
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->platform->dev;
struct earc *p_earc;
pr_info("asoc debug: %s\n", __func__);
p_earc = (struct earc *)dev_get_drvdata(dev);
snd_soc_set_runtime_hwparams(substream, &earc_hardware);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
p_earc->fddr = aml_audio_register_frddr(dev,
p_earc->actrl,
earc_ddr_isr, substream, false);
if (p_earc->fddr == NULL) {
dev_err(dev, "failed to claim from ddr\n");
return -ENXIO;
}
} else {
p_earc->tddr = aml_audio_register_toddr(dev,
p_earc->actrl,
earc_ddr_isr, substream);
if (p_earc->tddr == NULL) {
dev_err(dev, "failed to claim to ddr\n");
return -ENXIO;
}
}
runtime->private_data = p_earc;
return 0;
}
static int earc_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct earc *p_earc = runtime->private_data;
pr_info("asoc debug: %s\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
aml_audio_unregister_frddr(p_earc->dev, substream);
else
aml_audio_unregister_toddr(p_earc->dev, substream);
runtime->private_data = NULL;
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_trigger(struct snd_pcm_substream *substream, int cmd)
{
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;
start_addr = runtime->dma_addr;
end_addr = start_addr + runtime->dma_bytes - 8;
int_addr = frames_to_bytes(runtime, runtime->period_size) / 8;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
struct frddr *fr = p_earc->fddr;
aml_frddr_set_buf(fr, start_addr, end_addr);
aml_frddr_set_intrpt(fr, int_addr);
} else {
struct toddr *to = p_earc->tddr;
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;
}
int earc_silence(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
char *ppos;
int n;
n = frames_to_bytes(runtime, count);
ppos = runtime->dma_area + frames_to_bytes(runtime, pos);
memset(ppos, 0, n);
return 0;
}
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,
.trigger = earc_trigger,
.pointer = earc_pointer,
.silence = earc_silence,
.mmap = earc_mmap,
};
static int earc_new(struct snd_soc_pcm_runtime *rtd)
{
return snd_pcm_lib_preallocate_pages_for_all(
rtd->pcm, SNDRV_DMA_TYPE_DEV,
rtd->card->snd_card->dev,
PREALLOC_BUFFER_MAX,
PREALLOC_BUFFER_MAX);
}
struct snd_soc_platform_driver earc_platform = {
.ops = &earc_ops,
.pcm_new = earc_new,
};
static int earc_dai_probe(struct snd_soc_dai *cpu_dai)
{
pr_info("asoc debug: %s\n", __func__);
return 0;
}
static int earc_dai_remove(struct snd_soc_dai *cpu_dai)
{
return 0;
}
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);
unsigned int spdif_codec = spdif_get_codec();
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
struct frddr *fr = p_earc->fddr;
enum frddr_dest dst = EARCTX_DMAC;
unsigned int fifo_id, frddr_type = 0;
struct iec958_chsts chsts;
pr_info("%s Expected frddr dst:%s\n",
__func__,
frddr_src_get_str(dst));
switch (bit_depth) {
case 8:
frddr_type = 0;
break;
case 16:
frddr_type = 1;
break;
case 24:
frddr_type = 4;
break;
case 32:
frddr_type = 3;
break;
default:
pr_err("runtime format invalid bitwidth: %d\n",
bit_depth);
break;
}
fifo_id = aml_frddr_get_fifo_id(fr);
pr_info("%s, frddr_index:%d, bit_depth:%d, frddr_type:%d\n",
__func__,
fr->fifo_id, bit_depth, frddr_type);
aml_frddr_set_format(fr,
runtime->channels,
bit_depth - 1,
frddr_type);
aml_frddr_select_dst(fr, dst);
aml_frddr_set_fifos(fr, 0x40, 0x20);
earctx_dmac_init(p_earc->tx_top_map, p_earc->tx_dmac_map);
earctx_dmac_set_format(p_earc->tx_dmac_map,
fr->fifo_id,
bit_depth - 1,
frddr_type);
/* check and set channel status info */
spdif_get_channel_status_info(&chsts,
runtime->rate,
spdif_codec);
earctx_set_channel_status_info(p_earc->tx_dmac_map, &chsts);
} 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 or ARC is attended!\n");
return -ENOTCONN;
}
if (bit_depth == 32)
toddr_type = 3;
else if (bit_depth == 24)
toddr_type = 4;
else
toddr_type = 0;
msb = 28 - 1;
if (bit_depth == 16)
lsb = 28 - bit_depth;
else
lsb = 4;
pr_debug("%s Expected toddr src:%s, m:%d, n:%d, toddr type:%d\n",
__func__, toddr_src_get_str(src),
msb, lsb, toddr_type);
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);
aml_toddr_set_fifos(to, 0x40);
earcrx_dmac_init(p_earc->rx_top_map, p_earc->rx_dmac_map);
earcrx_arc_init(p_earc->rx_dmac_map);
}
return 0;
}
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,
true);
} 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,
false);
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 rate = params_rate(params);
int ret = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
int freq = rate * 128 * 5;
if (spdif_is_4x_clk()) {
pr_debug("set 4x audio clk for 958\n");
freq *= 4;
} else {
pr_debug("set normal 512 fs /4 fs\n");
}
clk_set_rate(p_earc->clk_tx_dmac_srcpll, freq * 4);
clk_set_rate(p_earc->clk_tx_dmac, freq);
pr_info("%s, tx dmac clk, set freq: %d, get freq:%lu\n",
__func__,
freq,
clk_get_rate(p_earc->clk_tx_dmac));
}
return ret;
}
static int earc_dai_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
struct earc *p_earc = snd_soc_dai_get_drvdata(cpu_dai);
pr_info("asoc earc_dai_set_fmt, %#x, %p\n", fmt, p_earc);
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);
pr_info("%s, %d, %d, %d\n",
__func__, clk_id, freq, dir);
if (clk_id == 1) {
clk_set_rate(p_earc->clk_rx_dmac, 500000000);
pr_info("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;
pr_info("%s\n", __func__);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* tx dmac clk */
if (!IS_ERR(p_earc->clk_tx_dmac)) {
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;
}
if (!IS_ERR(p_earc->clk_tx_dmac_srcpll)) {
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 {
/* rx dmac clk */
if (!IS_ERR(p_earc->clk_rx_dmac)) {
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;
}
p_earc->rx_dmac_clk_on = true;
}
if (!IS_ERR(p_earc->clk_rx_dmac_srcpll)) {
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);
}
return 0;
err:
pr_err("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);
pr_info("%s\n", __func__);
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;
}
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)) {
clk_disable_unprepare(p_earc->clk_rx_dmac);
p_earc->rx_dmac_clk_on = false;
}
if (!IS_ERR(p_earc->clk_rx_dmac_srcpll))
clk_disable_unprepare(p_earc->clk_rx_dmac_srcpll);
}
}
static struct snd_soc_dai_ops earc_dai_ops = {
.prepare = earc_dai_prepare,
.trigger = earc_dai_trigger,
.hw_params = earc_dai_hw_params,
.set_fmt = earc_dai_set_fmt,
.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,
.probe = earc_dai_probe,
.remove = earc_dai_remove,
.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"
};
const struct soc_enum attended_type_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(attended_type),
attended_type);
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 =
earcrx_cmdc_get_attended_type(p_earc->rx_cmdc_map);
ucontrol->value.integer.value[0] = type;
return 0;
}
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 = 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 =
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)
return 0;
earcrx_cmdc_arc_connect(
p_earc->rx_cmdc_map,
(bool)ucontrol->value.integer.value[0]);
return 0;
}
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_top_map))
return 0;
type = earctx_cmdc_get_attended_type(p_earc->tx_cmdc_map);
ucontrol->value.integer.value[0] = type;
return 0;
}
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_top_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;
}
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_top_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;
}
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_top_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;
}
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;
enum cmdc_st state;
int i;
u8 data[256];
if (!p_earc || IS_ERR(p_earc->rx_top_map))
return 0;
state = earcrx_cmdc_get_state(p_earc->rx_cmdc_map);
if (state != CMDC_ST_EARC)
return 0;
earcrx_cmdc_get_cds(p_earc->rx_cmdc_map, data);
for (i = 0; i < bytes_ext->max; i++)
*value++ = data[i];
return 0;
}
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);
struct soc_bytes_ext *params = (void *)kcontrol->private_value;
u8 *data;
enum cmdc_st state;
if (!p_earc || IS_ERR(p_earc->rx_top_map))
return 0;
state = earcrx_cmdc_get_state(p_earc->rx_cmdc_map);
if (state != CMDC_ST_EARC)
return 0;
data = kmemdup(ucontrol->value.bytes.data,
params->max, GFP_KERNEL | GFP_DMA);
if (!data)
return -ENOMEM;
earcrx_cmdc_set_cds(p_earc->rx_cmdc_map, data);
kfree(data);
return 0;
}
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_top_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;
}
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_top_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;
}
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_top_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;
}
static const struct snd_kcontrol_new earc_controls[] = {
SOC_SINGLE_BOOL_EXT("HDMI 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_INT("eARC_RX Latency",
earcrx_get_latency,
earcrx_set_latency),
SND_INT("eARC_TX Latency",
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),
};
static const struct snd_soc_component_driver earc_component = {
.controls = earc_controls,
.num_controls = ARRAY_SIZE(earc_controls),
.name = DRV_NAME,
};
static const struct of_device_id earc_device_id[] = {
{
.compatible = "amlogic, sm1-snd-earc",
},
{
.compatible = "amlogic, tm2-snd-earc",
},
{},
};
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)) {
pr_err("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 = extcon_dev_register(p_earc->rx_edev);
if (ret < 0) {
pr_err("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;
pr_info("%s, %s\n",
__func__,
st ? "plugin" : "plugout");
/* ensure clock gate */
audiobus_update_bits(EE_AUDIO_CLK_GATE_EN1, 0x1 << 6, 0x1 << 6);
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) {
pr_err("Can't enable earc clk_rx_cmdc: %d\n", ret);
return ret;
}
}
/* rx cmdc init */
earcrx_cmdc_init(p_earc->rx_top_map);
/* Default: arc arc_initiated */
earcrx_cmdc_arc_connect(p_earc->rx_cmdc_map, true);
ret = devm_request_threaded_irq(p_earc->dev,
p_earc->irq_earc_rx,
NULL,
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;
}
return ret;
}
static const unsigned int earctx_extcon[] = {
EXTCON_EARCTX_ATNDTYP_ARC,
EXTCON_EARCTX_ATNDTYP_EARC,
EXTCON_NONE,
};
static int earctx_extcon_register(struct earc *p_earc)
{
int ret = 0;
/* earc or arc connect */
p_earc->tx_edev = devm_extcon_dev_allocate(p_earc->dev, earctx_extcon);
if (IS_ERR(p_earc->tx_edev)) {
pr_err("failed to allocate earc extcon!!!\n");
ret = -ENOMEM;
return ret;
}
p_earc->tx_edev->dev.parent = p_earc->dev;
p_earc->tx_edev->name = "earctx";
dev_set_name(&p_earc->tx_edev->dev, "earctx");
ret = extcon_dev_register(p_earc->tx_edev);
if (ret < 0) {
pr_err("earc extcon failed to register!!\n");
return ret;
}
return ret;
}
void earc_hdmirx_hpdst(int earc_port, bool st)
{
struct earc *p_earc = s_earc;
if (!p_earc)
return;
pr_info("%s, earc_port:%d %s\n",
__func__,
earc_port,
st ? "plugin" : "plugout");
earctx_cmdc_int_mask(p_earc->tx_top_map);
earctx_cmdc_arc_connect(p_earc->tx_cmdc_map, st);
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) {
pr_err("Can't enable earc clk_tx_cmdc: %d\n", ret);
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;
}
/* tx cmdc init */
earctx_cmdc_init(p_earc->tx_top_map);
/* Default: arc arc_initiated */
earctx_cmdc_arc_connect(p_earc->tx_cmdc_map, true);
return ret;
}
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;
pr_info("%s\n", __func__);
p_earc = devm_kzalloc(dev, sizeof(struct earc), GFP_KERNEL);
if (!p_earc)
return -ENOMEM;
p_earc->dev = dev;
dev_set_drvdata(dev, p_earc);
/* get audio controller */
node_prt = of_get_parent(node);
if (node_prt == NULL)
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_err(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_err(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_err(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_err(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_err(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_err(dev,
"Can't get earcrx_top regmap!!\n");
}
/* clock gate */
p_earc->clk_rx_gate = devm_clk_get(&pdev->dev, "rx_gate");
if (IS_ERR(p_earc->clk_rx_gate)) {
dev_err(&pdev->dev,
"Can't get earc gate\n");
/*return PTR_ERR(p_earc->clk_rx_gate);*/
}
/* RX */
p_earc->clk_rx_cmdc = devm_clk_get(&pdev->dev, "rx_cmdc");
if (IS_ERR(p_earc->clk_rx_cmdc)) {
dev_err(&pdev->dev,
"Can't get clk_rx_cmdc\n");
return PTR_ERR(p_earc->clk_rx_cmdc);
}
p_earc->clk_rx_dmac = devm_clk_get(&pdev->dev, "rx_dmac");
if (IS_ERR(p_earc->clk_rx_dmac)) {
dev_err(&pdev->dev,
"Can't get clk_rx_dmac\n");
return PTR_ERR(p_earc->clk_rx_dmac);
}
p_earc->clk_rx_cmdc_srcpll = devm_clk_get(&pdev->dev, "rx_cmdc_srcpll");
if (IS_ERR(p_earc->clk_rx_cmdc_srcpll)) {
dev_err(&pdev->dev,
"Can't get clk_rx_cmdc_srcpll\n");
return PTR_ERR(p_earc->clk_rx_cmdc_srcpll);
}
p_earc->clk_rx_dmac_srcpll = devm_clk_get(&pdev->dev, "rx_dmac_srcpll");
if (IS_ERR(p_earc->clk_rx_dmac_srcpll)) {
dev_err(&pdev->dev,
"Can't get clk_rx_dmac_srcpll\n");
return PTR_ERR(p_earc->clk_rx_dmac_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");
ret = PTR_ERR(p_earc->clk_rx_cmdc);
return ret;
}
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");
ret = PTR_ERR(p_earc->clk_rx_dmac);
return ret;
}
/* TX */
p_earc->clk_tx_cmdc = devm_clk_get(&pdev->dev, "tx_cmdc");
if (IS_ERR(p_earc->clk_tx_cmdc)) {
dev_err(&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_err(&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_err(&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_err(&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");
ret = PTR_ERR(p_earc->clk_tx_cmdc);
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");
ret = PTR_ERR(p_earc->clk_tx_dmac);
return ret;
}
}
/* irqs */
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");
return p_earc->irq_earc_rx;
}
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, Check whether support eARC TX\n");
pr_info("%s, irq_earc_rx:%d, irq_earc_tx:%d\n",
__func__, p_earc->irq_earc_rx, 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;
}
s_earc = p_earc;
/* RX */
if (p_earc->irq_earc_rx > 0) {
earcrx_extcon_register(p_earc);
earcrx_cmdc_setup(p_earc);
}
/* TX */
if (p_earc->irq_earc_tx > 0) {
earctx_extcon_register(p_earc);
earctx_cmdc_setup(p_earc);
}
pr_info("%s, register soc platform\n", __func__);
return devm_snd_soc_register_platform(dev, &earc_platform);
}
struct platform_driver earc_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = earc_device_id,
},
.probe = earc_platform_probe,
};
static int __init earc_init(void)
{
return platform_driver_register(&earc_driver);
}
arch_initcall_sync(earc_init);
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);