Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
This commit is contained in:
Коммит
7b2f32cc81
|
@ -55,6 +55,7 @@ enum sst_audio_device_id_mrfld {
|
|||
PIPE_MEDIA0_IN = 0x8F,
|
||||
PIPE_MEDIA1_IN = 0x90,
|
||||
PIPE_MEDIA2_IN = 0x91,
|
||||
PIPE_MEDIA3_IN = 0x9C,
|
||||
PIPE_RSVD = 0xFF,
|
||||
};
|
||||
|
||||
|
|
|
@ -233,6 +233,15 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
|||
#define AZX_MLCTL_SPA (1<<16)
|
||||
#define AZX_MLCTL_CPA 23
|
||||
|
||||
|
||||
/* registers for DMA Resume Capability Structure */
|
||||
#define AZX_DRSM_CAP_ID 0x5
|
||||
#define AZX_REG_DRSM_CTL 0x4
|
||||
/* Base used to calculate the iterating register offset */
|
||||
#define AZX_DRSM_BASE 0x08
|
||||
/* Interval used to calculate the iterating register offset */
|
||||
#define AZX_DRSM_INTERVAL 0x08
|
||||
|
||||
/*
|
||||
* helpers to read the stream position
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
* @spbcap: SPIB capabilities pointer
|
||||
* @mlcap: MultiLink capabilities pointer
|
||||
* @gtscap: gts capabilities pointer
|
||||
* @drsmcap: dma resume capabilities pointer
|
||||
* @hlink_list: link list of HDA links
|
||||
*/
|
||||
struct hdac_ext_bus {
|
||||
|
@ -23,6 +24,7 @@ struct hdac_ext_bus {
|
|||
void __iomem *spbcap;
|
||||
void __iomem *mlcap;
|
||||
void __iomem *gtscap;
|
||||
void __iomem *drsmcap;
|
||||
|
||||
struct list_head hlink_list;
|
||||
};
|
||||
|
@ -72,6 +74,9 @@ enum hdac_ext_stream_type {
|
|||
* @pplc_addr: processing pipe link stream pointer
|
||||
* @spib_addr: software position in buffers stream pointer
|
||||
* @fifo_addr: software position Max fifos stream pointer
|
||||
* @dpibr_addr: DMA position in buffer resume pointer
|
||||
* @dpib: DMA position in buffer
|
||||
* @lpib: Linear position in buffer
|
||||
* @decoupled: stream host and link is decoupled
|
||||
* @link_locked: link is locked
|
||||
* @link_prepared: link is prepared
|
||||
|
@ -86,6 +91,10 @@ struct hdac_ext_stream {
|
|||
void __iomem *spib_addr;
|
||||
void __iomem *fifo_addr;
|
||||
|
||||
void __iomem *dpibr_addr;
|
||||
|
||||
u32 dpib;
|
||||
u32 lpib;
|
||||
bool decoupled:1;
|
||||
bool link_locked:1;
|
||||
bool link_prepared;
|
||||
|
@ -116,6 +125,11 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
|
|||
struct hdac_ext_stream *stream, u32 value);
|
||||
int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
|
||||
struct hdac_ext_stream *stream);
|
||||
void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
|
||||
bool enable, int index);
|
||||
int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
|
||||
struct hdac_ext_stream *stream, u32 value);
|
||||
int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value);
|
||||
|
||||
void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hstream);
|
||||
void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hstream);
|
||||
|
@ -133,6 +147,7 @@ struct hdac_ext_link {
|
|||
|
||||
int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link);
|
||||
int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link);
|
||||
int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus);
|
||||
int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus);
|
||||
void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
|
||||
int stream);
|
||||
|
@ -186,9 +201,15 @@ struct hdac_ext_device {
|
|||
/* codec ops */
|
||||
struct hdac_ext_codec_ops ops;
|
||||
|
||||
struct snd_card *card;
|
||||
void *scodec;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct hdac_ext_dma_params {
|
||||
u32 format;
|
||||
u8 stream_tag;
|
||||
};
|
||||
#define to_ehdac_device(dev) (container_of((dev), \
|
||||
struct hdac_ext_device, hdac))
|
||||
/*
|
||||
|
|
|
@ -49,6 +49,9 @@ struct device;
|
|||
#define SND_SOC_DAPM_SIGGEN(wname) \
|
||||
{ .id = snd_soc_dapm_siggen, .name = wname, .kcontrol_news = NULL, \
|
||||
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
|
||||
#define SND_SOC_DAPM_SINK(wname) \
|
||||
{ .id = snd_soc_dapm_sink, .name = wname, .kcontrol_news = NULL, \
|
||||
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
|
||||
#define SND_SOC_DAPM_INPUT(wname) \
|
||||
{ .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
|
||||
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
|
||||
|
@ -485,6 +488,7 @@ enum snd_soc_dapm_type {
|
|||
snd_soc_dapm_aif_in, /* audio interface input */
|
||||
snd_soc_dapm_aif_out, /* audio interface output */
|
||||
snd_soc_dapm_siggen, /* signal generator */
|
||||
snd_soc_dapm_sink,
|
||||
snd_soc_dapm_dai_in, /* link to DAI structure */
|
||||
snd_soc_dapm_dai_out,
|
||||
snd_soc_dapm_dai_link, /* link between two DAI structures */
|
||||
|
|
|
@ -1106,7 +1106,7 @@ struct snd_soc_card {
|
|||
/* CPU <--> Codec DAI links */
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
int num_links;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct list_head rtd_list;
|
||||
int num_rtd;
|
||||
|
||||
/* optional codec specific configuration */
|
||||
|
@ -1201,6 +1201,9 @@ struct snd_soc_pcm_runtime {
|
|||
struct dentry *debugfs_dpcm_root;
|
||||
struct dentry *debugfs_dpcm_state;
|
||||
#endif
|
||||
|
||||
unsigned int num; /* 0-based and monotonic increasing */
|
||||
struct list_head list; /* rtd list of the soc card */
|
||||
};
|
||||
|
||||
/* mixer control */
|
||||
|
|
|
@ -77,6 +77,12 @@ int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus)
|
|||
ebus->spbcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_DRSM_CAP_ID:
|
||||
/* DMA resume capability found, handler function */
|
||||
dev_dbg(bus->dev, "Found DRSM capability\n");
|
||||
ebus->drsmcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
|
||||
break;
|
||||
|
@ -240,7 +246,7 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
|
|||
int mask = (1 << AZX_MLCTL_CPA);
|
||||
|
||||
udelay(3);
|
||||
timeout = 50;
|
||||
timeout = 150;
|
||||
|
||||
do {
|
||||
val = readl(link->ml_addr + AZX_REG_ML_LCTL);
|
||||
|
@ -281,6 +287,27 @@ int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_bus_link_power_up_all -power up all hda link
|
||||
* @ebus: HD-audio extended bus
|
||||
*/
|
||||
int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
struct hdac_ext_link *hlink = NULL;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(hlink, &ebus->hlink_list, list) {
|
||||
snd_hdac_updatel(hlink->ml_addr,
|
||||
AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
|
||||
ret = check_hdac_link_power_active(hlink, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_bus_link_power_down_all -power down all hda link
|
||||
* @ebus: HD-audio extended bus
|
||||
|
|
|
@ -59,6 +59,10 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
|
|||
AZX_SPB_MAXFIFO;
|
||||
}
|
||||
|
||||
if (ebus->drsmcap)
|
||||
stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE +
|
||||
AZX_DRSM_INTERVAL * idx;
|
||||
|
||||
stream->decoupled = false;
|
||||
snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
|
||||
}
|
||||
|
@ -107,6 +111,7 @@ void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus)
|
|||
while (!list_empty(&bus->stream_list)) {
|
||||
s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
|
||||
stream = stream_to_hdac_ext_stream(s);
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, false);
|
||||
list_del(&s->list);
|
||||
kfree(stream);
|
||||
}
|
||||
|
@ -497,3 +502,70 @@ void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus)
|
|||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream
|
||||
* @ebus: HD-audio ext core bus
|
||||
* @enable: flag to enable/disable DRSM
|
||||
* @index: stream index for which DRSM need to be enabled
|
||||
*/
|
||||
void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
|
||||
bool enable, int index)
|
||||
{
|
||||
u32 mask = 0;
|
||||
u32 register_mask = 0;
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->drsmcap) {
|
||||
dev_err(bus->dev, "Address of DRSM capability is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
mask |= (1 << index);
|
||||
|
||||
register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL);
|
||||
|
||||
mask |= register_mask;
|
||||
|
||||
if (enable)
|
||||
snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
|
||||
else
|
||||
snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream
|
||||
* @ebus: HD-audio ext core bus
|
||||
* @stream: hdac_ext_stream
|
||||
* @value: dpib value to set
|
||||
*/
|
||||
int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
|
||||
struct hdac_ext_stream *stream, u32 value)
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->drsmcap) {
|
||||
dev_err(bus->dev, "Address of DRSM capability is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(value, stream->dpibr_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr);
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream
|
||||
* @ebus: HD-audio ext core bus
|
||||
* @stream: hdac_ext_stream
|
||||
* @value: lpib value to set
|
||||
*/
|
||||
int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value)
|
||||
{
|
||||
snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib);
|
||||
|
|
|
@ -66,6 +66,7 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_ES8328_SPI if SPI_MASTER
|
||||
select SND_SOC_ES8328_I2C if I2C
|
||||
select SND_SOC_GTM601
|
||||
select SND_SOC_HDAC_HDMI
|
||||
select SND_SOC_ICS43432
|
||||
select SND_SOC_ISABELLE if I2C
|
||||
select SND_SOC_JZ4740_CODEC
|
||||
|
@ -468,6 +469,11 @@ config SND_SOC_ES8328_SPI
|
|||
config SND_SOC_GTM601
|
||||
tristate 'GTM601 UMTS modem audio codec'
|
||||
|
||||
config SND_SOC_HDAC_HDMI
|
||||
tristate
|
||||
select SND_HDA_EXT_CORE
|
||||
select HDMI
|
||||
|
||||
config SND_SOC_ICS43432
|
||||
tristate
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ snd-soc-es8328-objs := es8328.o
|
|||
snd-soc-es8328-i2c-objs := es8328-i2c.o
|
||||
snd-soc-es8328-spi-objs := es8328-spi.o
|
||||
snd-soc-gtm601-objs := gtm601.o
|
||||
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
|
||||
snd-soc-ics43432-objs := ics43432.o
|
||||
snd-soc-isabelle-objs := isabelle.o
|
||||
snd-soc-jz4740-codec-objs := jz4740.o
|
||||
|
@ -254,6 +255,7 @@ obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
|
|||
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
|
||||
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
|
||||
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
|
||||
|
|
|
@ -0,0 +1,697 @@
|
|||
/*
|
||||
* hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
|
||||
*
|
||||
* Copyright (C) 2014-2015 Intel Corp
|
||||
* Author: Samreen Nilofer <samreen.nilofer@intel.com>
|
||||
* Subhransu S. Prusty <subhransu.s.prusty@intel.com>
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include "../../hda/local.h"
|
||||
|
||||
#define AMP_OUT_MUTE 0xb080
|
||||
#define AMP_OUT_UNMUTE 0xb000
|
||||
#define PIN_OUT (AC_PINCTL_OUT_EN)
|
||||
|
||||
#define HDA_MAX_CONNECTIONS 32
|
||||
|
||||
struct hdac_hdmi_cvt_params {
|
||||
unsigned int channels_min;
|
||||
unsigned int channels_max;
|
||||
u32 rates;
|
||||
u64 formats;
|
||||
unsigned int maxbps;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_cvt {
|
||||
struct list_head head;
|
||||
hda_nid_t nid;
|
||||
struct hdac_hdmi_cvt_params params;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_pin {
|
||||
struct list_head head;
|
||||
hda_nid_t nid;
|
||||
int num_mux_nids;
|
||||
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
|
||||
};
|
||||
|
||||
struct hdac_hdmi_dai_pin_map {
|
||||
int dai_id;
|
||||
struct hdac_hdmi_pin *pin;
|
||||
struct hdac_hdmi_cvt *cvt;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_priv {
|
||||
struct hdac_hdmi_dai_pin_map dai_map[3];
|
||||
struct list_head pin_list;
|
||||
struct list_head cvt_list;
|
||||
int num_pin;
|
||||
int num_cvt;
|
||||
};
|
||||
|
||||
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
|
||||
{
|
||||
struct hdac_device *hdac = dev_to_hdac_dev(dev);
|
||||
|
||||
return to_ehdac_device(hdac);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
|
||||
hda_nid_t cvt_nid, hda_nid_t pin_nid,
|
||||
u32 stream_tag, int format)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
|
||||
cvt_nid, pin_nid, stream_tag, format);
|
||||
|
||||
val = (stream_tag << 4);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, val);
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
|
||||
int packet_index, int byte_index)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = (packet_index << 5) | (byte_index & 0x1f);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_INDEX, val);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
|
||||
hda_nid_t cvt_nid, hda_nid_t pin_nid)
|
||||
{
|
||||
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
|
||||
struct hdmi_audio_infoframe frame;
|
||||
u8 *dip = (u8 *)&frame;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
hdmi_audio_infoframe_init(&frame);
|
||||
|
||||
/* Default stereo for now */
|
||||
frame.channels = 2;
|
||||
|
||||
/* setup channel count */
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
|
||||
|
||||
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* stop infoframe transmission */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
|
||||
|
||||
|
||||
/* Fill infoframe. Index auto-incremented */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(frame); i++)
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
|
||||
|
||||
/* Start infoframe */
|
||||
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
|
||||
snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
|
||||
AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
|
||||
struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
|
||||
{
|
||||
/* Power up pin widget */
|
||||
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid,
|
||||
pwr_state))
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_SET_POWER_STATE, pwr_state);
|
||||
|
||||
/* Power up converter */
|
||||
if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
|
||||
pwr_state))
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
|
||||
AC_VERB_SET_POWER_STATE, pwr_state);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
struct hdac_ext_dma_params *dd;
|
||||
int ret;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
||||
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
|
||||
dd->stream_tag, dd->format);
|
||||
|
||||
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
|
||||
dai_map->pin->nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
|
||||
dai_map->pin->nid, dd->stream_tag, dd->format);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_ext_dma_params *dd;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
|
||||
params_channels(hparams), params_format(hparams),
|
||||
24, 0);
|
||||
|
||||
snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_ext_dma_params *dd;
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
|
||||
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
|
||||
kfree(dd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
int val;
|
||||
|
||||
if (dai->id > 0) {
|
||||
dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
|
||||
|
||||
if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
|
||||
dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
|
||||
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdac_hdmi_priv *hdmi = hdac->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map;
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
|
||||
hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
|
||||
|
||||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
}
|
||||
|
||||
static int
|
||||
hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Only stereo supported as of now */
|
||||
cvt->params.channels_min = cvt->params.channels_max = 2;
|
||||
|
||||
err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
|
||||
&cvt->params.rates,
|
||||
&cvt->params.formats,
|
||||
&cvt->params.maxbps);
|
||||
if (err < 0)
|
||||
dev_err(&hdac->dev,
|
||||
"Failed to query pcm params for nid %d: %d\n",
|
||||
cvt->nid, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
|
||||
enum snd_soc_dapm_type id,
|
||||
const char *wname, const char *stream)
|
||||
{
|
||||
w->id = id;
|
||||
w->name = wname;
|
||||
w->sname = stream;
|
||||
w->reg = SND_SOC_NOPM;
|
||||
w->shift = 0;
|
||||
w->kcontrol_news = NULL;
|
||||
w->num_kcontrols = 0;
|
||||
w->priv = NULL;
|
||||
}
|
||||
|
||||
static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
|
||||
const char *sink, const char *control, const char *src)
|
||||
{
|
||||
route->sink = sink;
|
||||
route->source = src;
|
||||
route->control = control;
|
||||
route->connected = NULL;
|
||||
}
|
||||
|
||||
static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
|
||||
struct hdac_hdmi_dai_pin_map *dai_map)
|
||||
{
|
||||
struct snd_soc_dapm_route route[1];
|
||||
struct snd_soc_dapm_widget widgets[2] = { {0} };
|
||||
|
||||
memset(&route, 0, sizeof(route));
|
||||
|
||||
hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
|
||||
"hif1 Output", NULL);
|
||||
hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
|
||||
"Coverter 1", "hif1");
|
||||
|
||||
hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
|
||||
|
||||
snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
|
||||
snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
|
||||
}
|
||||
|
||||
static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
|
||||
struct hdac_hdmi_cvt *cvt;
|
||||
struct hdac_hdmi_pin *pin;
|
||||
|
||||
if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Currently on board only 1 pin and 1 converter is enabled for
|
||||
* simplification, more will be added eventually
|
||||
* So using fixed map for dai_id:pin:cvt
|
||||
*/
|
||||
cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
|
||||
pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
|
||||
|
||||
dai_map->dai_id = 0;
|
||||
dai_map->pin = pin;
|
||||
|
||||
dai_map->cvt = cvt;
|
||||
|
||||
/* Enable out path for this pin widget */
|
||||
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
|
||||
/* Enable transmission */
|
||||
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1, 1);
|
||||
|
||||
/* Category Code (CC) to zero */
|
||||
snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, 0);
|
||||
|
||||
snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
|
||||
AC_VERB_SET_CONNECT_SEL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_cvt *cvt;
|
||||
|
||||
cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
|
||||
if (!cvt)
|
||||
return -ENOMEM;
|
||||
|
||||
cvt->nid = nid;
|
||||
|
||||
list_add_tail(&cvt->head, &hdmi->cvt_list);
|
||||
hdmi->num_cvt++;
|
||||
|
||||
return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
|
||||
}
|
||||
|
||||
static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pin *pin;
|
||||
|
||||
pin = kzalloc(sizeof(*pin), GFP_KERNEL);
|
||||
if (!pin)
|
||||
return -ENOMEM;
|
||||
|
||||
pin->nid = nid;
|
||||
|
||||
list_add_tail(&pin->head, &hdmi->pin_list);
|
||||
hdmi->num_pin++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse all nodes and store the cvt/pin nids in array
|
||||
* Add one time initialization for pin and cvt widgets
|
||||
*/
|
||||
static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
|
||||
{
|
||||
hda_nid_t nid;
|
||||
int i, num_nodes;
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
int ret;
|
||||
|
||||
num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
|
||||
if (!nid || num_nodes <= 0) {
|
||||
dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdac->num_nodes = num_nodes;
|
||||
hdac->start_nid = nid;
|
||||
|
||||
for (i = 0; i < hdac->num_nodes; i++, nid++) {
|
||||
unsigned int caps;
|
||||
unsigned int type;
|
||||
|
||||
caps = get_wcaps(hdac, nid);
|
||||
type = get_wcaps_type(caps);
|
||||
|
||||
if (!(caps & AC_WCAP_DIGITAL))
|
||||
continue;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case AC_WID_AUD_OUT:
|
||||
ret = hdac_hdmi_add_cvt(edev, nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case AC_WID_PIN:
|
||||
ret = hdac_hdmi_add_pin(edev, nid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hdac->end_nid = nid;
|
||||
|
||||
if (!hdmi->num_pin || !hdmi->num_cvt)
|
||||
return -EIO;
|
||||
|
||||
return hdac_hdmi_init_dai_map(edev);
|
||||
}
|
||||
|
||||
static int hdmi_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(&codec->component);
|
||||
|
||||
edev->scodec = codec;
|
||||
|
||||
create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
|
||||
|
||||
/* Imp: Store the card pointer in hda_codec */
|
||||
edev->card = dapm->card->snd_card;
|
||||
|
||||
/*
|
||||
* hdac_device core already sets the state to active and calls
|
||||
* get_noresume. So enable runtime and set the device to suspend.
|
||||
*/
|
||||
pm_runtime_enable(&edev->hdac.dev);
|
||||
pm_runtime_put(&edev->hdac.dev);
|
||||
pm_runtime_suspend(&edev->hdac.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
pm_runtime_disable(&edev->hdac.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver hdmi_hda_codec = {
|
||||
.probe = hdmi_codec_probe,
|
||||
.remove = hdmi_codec_remove,
|
||||
.idle_bias_off = true,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops hdmi_dai_ops = {
|
||||
.startup = hdac_hdmi_pcm_open,
|
||||
.shutdown = hdac_hdmi_pcm_close,
|
||||
.hw_params = hdac_hdmi_set_hw_params,
|
||||
.prepare = hdac_hdmi_playback_prepare,
|
||||
.hw_free = hdac_hdmi_playback_cleanup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver hdmi_dais[] = {
|
||||
{ .name = "intel-hdmi-hif1",
|
||||
.playback = {
|
||||
.stream_name = "hif1",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
|
||||
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S20_3LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
|
||||
},
|
||||
.ops = &hdmi_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_device *codec = &edev->hdac;
|
||||
struct hdac_hdmi_priv *hdmi_priv;
|
||||
int ret = 0;
|
||||
|
||||
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
|
||||
if (hdmi_priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
edev->private_data = hdmi_priv;
|
||||
|
||||
dev_set_drvdata(&codec->dev, edev);
|
||||
|
||||
INIT_LIST_HEAD(&hdmi_priv->pin_list);
|
||||
INIT_LIST_HEAD(&hdmi_priv->cvt_list);
|
||||
|
||||
ret = hdac_hdmi_parse_and_map_nid(edev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* ASoC specific initialization */
|
||||
return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
|
||||
hdmi_dais, ARRAY_SIZE(hdmi_dais));
|
||||
}
|
||||
|
||||
static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pin *pin, *pin_next;
|
||||
struct hdac_hdmi_cvt *cvt, *cvt_next;
|
||||
|
||||
snd_soc_unregister_codec(&edev->hdac.dev);
|
||||
|
||||
list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
|
||||
list_del(&cvt->head);
|
||||
kfree(cvt);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) {
|
||||
list_del(&pin->head);
|
||||
kfree(pin);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hdac_hdmi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
|
||||
/* controller may not have been initialized for the first time */
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
/* Power down afg */
|
||||
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
|
||||
snd_hdac_codec_write(hdac, hdac->afg, 0,
|
||||
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
|
||||
|
||||
err = snd_hdac_display_power(bus, false);
|
||||
if (err < 0) {
|
||||
dev_err(bus->dev, "Cannot turn on display power on i915\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_hda_ext_device(dev);
|
||||
struct hdac_device *hdac = &edev->hdac;
|
||||
struct hdac_bus *bus = hdac->bus;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "Enter: %s\n", __func__);
|
||||
|
||||
/* controller may not have been initialized for the first time */
|
||||
if (!bus)
|
||||
return 0;
|
||||
|
||||
err = snd_hdac_display_power(bus, true);
|
||||
if (err < 0) {
|
||||
dev_err(bus->dev, "Cannot turn on display power on i915\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Power up afg */
|
||||
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
|
||||
snd_hdac_codec_write(hdac, hdac->afg, 0,
|
||||
AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define hdac_hdmi_runtime_suspend NULL
|
||||
#define hdac_hdmi_runtime_resume NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops hdac_hdmi_pm = {
|
||||
SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct hda_device_id hdmi_list[] = {
|
||||
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
|
||||
|
||||
static struct hdac_ext_driver hdmi_driver = {
|
||||
. hdac = {
|
||||
.driver = {
|
||||
.name = "HDMI HDA Codec",
|
||||
.pm = &hdac_hdmi_pm,
|
||||
},
|
||||
.id_table = hdmi_list,
|
||||
},
|
||||
.probe = hdac_hdmi_dev_probe,
|
||||
.remove = hdac_hdmi_dev_remove,
|
||||
};
|
||||
|
||||
static int __init hdmi_init(void)
|
||||
{
|
||||
return snd_hda_ext_driver_register(&hdmi_driver);
|
||||
}
|
||||
|
||||
static void __exit hdmi_exit(void)
|
||||
{
|
||||
snd_hda_ext_driver_unregister(&hdmi_driver);
|
||||
}
|
||||
|
||||
module_init(hdmi_init);
|
||||
module_exit(hdmi_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("HDMI HD codec");
|
||||
MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
|
||||
MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
|
|
@ -488,6 +488,18 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int is_using_asrc(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
|
||||
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (!rt5640->asrc_en)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Digital Mixer */
|
||||
static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = {
|
||||
SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER,
|
||||
|
@ -1059,6 +1071,20 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
|
|||
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
|
||||
RT5640_PWR_PLL_BIT, 0, NULL, 0),
|
||||
|
||||
/* ASRC */
|
||||
SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1,
|
||||
15, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("I2S2 Filter ASRC", 1, RT5640_ASRC_1,
|
||||
12, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5640_ASRC_1,
|
||||
11, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DMIC1 ASRC", 1, RT5640_ASRC_1,
|
||||
9, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DMIC2 ASRC", 1, RT5640_ASRC_1,
|
||||
8, 0, NULL, 0),
|
||||
|
||||
|
||||
/* Input Side */
|
||||
/* micbias */
|
||||
SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1,
|
||||
|
@ -1319,6 +1345,12 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
|
|||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
|
||||
{ "I2S1", NULL, "Stereo Filter ASRC", is_using_asrc },
|
||||
{ "I2S2", NULL, "I2S2 ASRC", is_using_asrc },
|
||||
{ "I2S2", NULL, "I2S2 Filter ASRC", is_using_asrc },
|
||||
{ "DMIC1", NULL, "DMIC1 ASRC", is_using_asrc },
|
||||
{ "DMIC2", NULL, "DMIC2 ASRC", is_using_asrc },
|
||||
|
||||
{"IN1P", NULL, "LDO2"},
|
||||
{"IN2P", NULL, "LDO2"},
|
||||
{"IN3P", NULL, "LDO2"},
|
||||
|
@ -1981,6 +2013,76 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
|
||||
|
||||
int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src)
|
||||
{
|
||||
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int asrc2_mask = 0;
|
||||
unsigned int asrc2_value = 0;
|
||||
|
||||
switch (clk_src) {
|
||||
case RT5640_CLK_SEL_SYS:
|
||||
case RT5640_CLK_SEL_ASRC:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!filter_mask)
|
||||
return -EINVAL;
|
||||
|
||||
if (filter_mask & RT5640_DA_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5640_STO_DAC_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_STO_DAC_M_MASK)
|
||||
| (clk_src << RT5640_STO_DAC_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_DA_MONO_L_FILTER) {
|
||||
asrc2_mask |= RT5640_MDA_L_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MDA_L_M_MASK)
|
||||
| (clk_src << RT5640_MDA_L_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_DA_MONO_R_FILTER) {
|
||||
asrc2_mask |= RT5640_MDA_R_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MDA_R_M_MASK)
|
||||
| (clk_src << RT5640_MDA_R_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_AD_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5640_ADC_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_ADC_M_MASK)
|
||||
| (clk_src << RT5640_ADC_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_AD_MONO_L_FILTER) {
|
||||
asrc2_mask |= RT5640_MAD_L_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MAD_L_M_MASK)
|
||||
| (clk_src << RT5640_MAD_L_M_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5640_AD_MONO_R_FILTER) {
|
||||
asrc2_mask |= RT5640_MAD_R_M_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5640_MAD_R_M_MASK)
|
||||
| (clk_src << RT5640_MAD_R_M_SFT);
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, RT5640_ASRC_2,
|
||||
asrc2_mask, asrc2_value);
|
||||
|
||||
if (snd_soc_read(codec, RT5640_ASRC_2)) {
|
||||
rt5640->asrc_en = true;
|
||||
snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x3);
|
||||
} else {
|
||||
rt5640->asrc_en = false;
|
||||
snd_soc_update_bits(codec, RT5640_JD_CTRL, 0x3, 0x0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
|
||||
|
||||
static int rt5640_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
|
@ -2175,6 +2277,7 @@ static const struct acpi_device_id rt5640_acpi_match[] = {
|
|||
{ "INT33CA", 0 },
|
||||
{ "10EC5640", 0 },
|
||||
{ "10EC5642", 0 },
|
||||
{ "INTCCFFD", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
|
||||
|
|
|
@ -1033,6 +1033,10 @@
|
|||
#define RT5640_DMIC_2_M_NOR (0x0 << 8)
|
||||
#define RT5640_DMIC_2_M_ASYN (0x1 << 8)
|
||||
|
||||
/* ASRC clock source selection (0x84) */
|
||||
#define RT5640_CLK_SEL_SYS (0x0)
|
||||
#define RT5640_CLK_SEL_ASRC (0x1)
|
||||
|
||||
/* ASRC Control 2 (0x84) */
|
||||
#define RT5640_MDA_L_M_MASK (0x1 << 15)
|
||||
#define RT5640_MDA_L_M_SFT 15
|
||||
|
@ -2079,6 +2083,16 @@ enum {
|
|||
RT5640_DMIC2,
|
||||
};
|
||||
|
||||
/* filter mask */
|
||||
enum {
|
||||
RT5640_DA_STEREO_FILTER = 0x1,
|
||||
RT5640_DA_MONO_L_FILTER = (0x1 << 1),
|
||||
RT5640_DA_MONO_R_FILTER = (0x1 << 2),
|
||||
RT5640_AD_STEREO_FILTER = (0x1 << 3),
|
||||
RT5640_AD_MONO_L_FILTER = (0x1 << 4),
|
||||
RT5640_AD_MONO_R_FILTER = (0x1 << 5),
|
||||
};
|
||||
|
||||
struct rt5640_priv {
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt5640_platform_data pdata;
|
||||
|
@ -2095,9 +2109,12 @@ struct rt5640_priv {
|
|||
int pll_out;
|
||||
|
||||
bool hp_mute;
|
||||
bool asrc_en;
|
||||
};
|
||||
|
||||
int rt5640_dmic_enable(struct snd_soc_codec *codec,
|
||||
bool dmic1_data_pin, bool dmic2_data_pin);
|
||||
int rt5640_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -222,12 +222,15 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
|
|||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct codec_priv *codec_priv = &priv->codec_priv;
|
||||
struct device *dev = card->dev;
|
||||
unsigned int pll_out;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -69,13 +69,16 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct imx_priv *priv = &card_priv;
|
||||
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
unsigned int pll_out;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -135,12 +138,15 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
|
|||
|
||||
static int imx_wm8962_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct imx_priv *priv = &card_priv;
|
||||
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
|
||||
data->clk_frequency, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -45,7 +45,7 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct simple_dai_props *dai_props =
|
||||
&priv->dai_props[rtd - rtd->card->rtd];
|
||||
&priv->dai_props[rtd->num];
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dai_props->cpu_dai.clk);
|
||||
|
@ -64,7 +64,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct simple_dai_props *dai_props =
|
||||
&priv->dai_props[rtd - rtd->card->rtd];
|
||||
&priv->dai_props[rtd->num];
|
||||
|
||||
clk_disable_unprepare(dai_props->cpu_dai.clk);
|
||||
|
||||
|
@ -78,8 +78,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct simple_dai_props *dai_props =
|
||||
&priv->dai_props[rtd - rtd->card->rtd];
|
||||
struct simple_dai_props *dai_props = &priv->dai_props[rtd->num];
|
||||
unsigned int mclk, mclk_fs = 0;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -174,10 +173,9 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
struct snd_soc_dai *codec = rtd->codec_dai;
|
||||
struct snd_soc_dai *cpu = rtd->cpu_dai;
|
||||
struct simple_dai_props *dai_props;
|
||||
int num, ret;
|
||||
int ret;
|
||||
|
||||
num = rtd - rtd->card->rtd;
|
||||
dai_props = &priv->dai_props[num];
|
||||
dai_props = &priv->dai_props[rtd->num];
|
||||
ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -24,6 +24,7 @@ config SND_SST_IPC_PCI
|
|||
config SND_SST_IPC_ACPI
|
||||
tristate
|
||||
select SND_SST_IPC
|
||||
select SND_SOC_INTEL_SST
|
||||
depends on ACPI
|
||||
|
||||
config SND_SOC_INTEL_SST
|
||||
|
@ -43,7 +44,7 @@ config SND_SOC_INTEL_BAYTRAIL
|
|||
config SND_SOC_INTEL_HASWELL_MACH
|
||||
tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
|
||||
depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_HASWELL
|
||||
select SND_SOC_RT5640
|
||||
|
@ -56,18 +57,19 @@ config SND_SOC_INTEL_HASWELL_MACH
|
|||
config SND_SOC_INTEL_BYT_RT5640_MACH
|
||||
tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y && (SND_SOC_INTEL_BYTCR_RT5640_MACH = n)
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_BAYTRAIL
|
||||
select SND_SOC_RT5640
|
||||
help
|
||||
This adds audio driver for Intel Baytrail platform based boards
|
||||
with the RT5640 audio codec.
|
||||
with the RT5640 audio codec. This driver is deprecated, use
|
||||
SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality
|
||||
|
||||
config SND_SOC_INTEL_BYT_MAX98090_MACH
|
||||
tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_BAYTRAIL
|
||||
select SND_SOC_MAX98090
|
||||
|
@ -79,7 +81,7 @@ config SND_SOC_INTEL_BROADWELL_MACH
|
|||
tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
|
||||
depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
|
||||
I2C_DESIGNWARE_PLATFORM
|
||||
depends on DW_DMAC_CORE
|
||||
depends on DW_DMAC_CORE=y
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_HASWELL
|
||||
select SND_SOC_RT286
|
||||
|
@ -90,14 +92,26 @@ config SND_SOC_INTEL_BROADWELL_MACH
|
|||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_BYTCR_RT5640_MACH
|
||||
tristate "ASoC Audio DSP Support for MID BYT Platform"
|
||||
tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"
|
||||
depends on X86 && I2C
|
||||
select SND_SOC_RT5640
|
||||
select SND_SST_MFLD_PLATFORM
|
||||
select SND_SST_IPC_ACPI
|
||||
help
|
||||
This adds support for ASoC machine driver for Intel(R) MID Baytrail platform
|
||||
used as alsa device in audio substem in Intel(R) MID devices
|
||||
This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
|
||||
platforms with RT5640 audio codec.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_BYTCR_RT5651_MACH
|
||||
tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec"
|
||||
depends on X86 && I2C
|
||||
select SND_SOC_RT5651
|
||||
select SND_SST_MFLD_PLATFORM
|
||||
select SND_SST_IPC_ACPI
|
||||
help
|
||||
This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
|
||||
platforms with RT5651 audio codec.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
|
@ -154,3 +168,31 @@ config SND_SOC_INTEL_SKL_RT286_MACH
|
|||
with RT286 I2S audio codec.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
|
||||
tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_SKYLAKE
|
||||
select SND_SOC_NAU8825
|
||||
select SND_SOC_SSM4567
|
||||
select SND_SOC_DMIC
|
||||
help
|
||||
This adds support for ASoC Onboard Codec I2S machine driver. This will
|
||||
create an alsa sound card for NAU88L25 + SSM4567.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
|
||||
tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode"
|
||||
depends on X86_INTEL_LPSS && I2C
|
||||
select SND_SOC_INTEL_SST
|
||||
select SND_SOC_INTEL_SKYLAKE
|
||||
select SND_SOC_NAU8825
|
||||
select SND_SOC_MAX98357A
|
||||
select SND_SOC_DMIC
|
||||
help
|
||||
This adds support for ASoC Onboard Codec I2S machine driver. This will
|
||||
create an alsa sound card for NAU88L25 + MAX98357A.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
|
|
@ -443,7 +443,7 @@ static int sst_gain_get(struct snd_kcontrol *kcontrol,
|
|||
break;
|
||||
|
||||
case SST_GAIN_MUTE:
|
||||
ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
|
||||
ucontrol->value.integer.value[0] = gv->mute ? 0 : 1;
|
||||
break;
|
||||
|
||||
case SST_GAIN_RAMP_DURATION:
|
||||
|
@ -479,7 +479,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol,
|
|||
break;
|
||||
|
||||
case SST_GAIN_MUTE:
|
||||
gv->mute = !!ucontrol->value.integer.value[0];
|
||||
gv->mute = !ucontrol->value.integer.value[0];
|
||||
dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
|
||||
break;
|
||||
|
||||
|
@ -1109,6 +1109,7 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"media0_in", NULL, "Compress Playback"},
|
||||
{"media1_in", NULL, "Headset Playback"},
|
||||
{"media2_in", NULL, "pcm0_out"},
|
||||
{"media3_in", NULL, "Deepbuffer Playback"},
|
||||
|
||||
{"media0_out mix 0", "media0_in Switch", "media0_in"},
|
||||
{"media0_out mix 0", "media1_in Switch", "media1_in"},
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
enum {
|
||||
MERR_DPCM_AUDIO = 0,
|
||||
MERR_DPCM_DEEP_BUFFER,
|
||||
MERR_DPCM_COMPR,
|
||||
};
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ static struct sst_dev_stream_map dpcm_strm_map[] = {
|
|||
{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
|
||||
{MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
|
||||
{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
|
||||
{MERR_DPCM_DEEP_BUFFER, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA3_IN, SST_TASK_ID_MEDIA, 0},
|
||||
};
|
||||
|
||||
static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
|
||||
|
@ -500,14 +501,25 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
|
|||
.channels_min = SST_STEREO,
|
||||
.channels_max = SST_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Headset Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "deepbuffer-cpu-dai",
|
||||
.ops = &sst_media_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "Deepbuffer Playback",
|
||||
.channels_min = SST_STEREO,
|
||||
.channels_max = SST_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -516,10 +528,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
|
|||
.ops = &sst_compr_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "Compress Playback",
|
||||
.channels_min = SST_STEREO,
|
||||
.channels_max = SST_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
/* BE CPU Dais */
|
||||
|
@ -760,15 +768,15 @@ static int sst_platform_remove(struct platform_device *pdev)
|
|||
static int sst_soc_prepare(struct device *dev)
|
||||
{
|
||||
struct sst_data *drv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
/* suspend all pcms first */
|
||||
snd_soc_suspend(drv->soc_card->dev);
|
||||
snd_soc_poweroff(drv->soc_card->dev);
|
||||
|
||||
/* set the SSPs to idle */
|
||||
for (i = 0; i < drv->soc_card->num_rtd; i++) {
|
||||
struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
|
||||
list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
|
||||
if (dai->active) {
|
||||
send_ssp_cmd(dai, dai->name, 0);
|
||||
|
@ -782,11 +790,11 @@ static int sst_soc_prepare(struct device *dev)
|
|||
static void sst_soc_complete(struct device *dev)
|
||||
{
|
||||
struct sst_data *drv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
/* restart SSPs */
|
||||
for (i = 0; i < drv->soc_card->num_rtd; i++) {
|
||||
struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
|
||||
list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) {
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
|
||||
if (dai->active) {
|
||||
sst_handle_vb_timer(dai, true);
|
||||
|
|
|
@ -40,18 +40,9 @@
|
|||
#include <acpi/acpi_bus.h>
|
||||
#include "../sst-mfld-platform.h"
|
||||
#include "../../common/sst-dsp.h"
|
||||
#include "../../common/sst-acpi.h"
|
||||
#include "sst.h"
|
||||
|
||||
struct sst_machines {
|
||||
char *codec_id;
|
||||
char board[32];
|
||||
char machine[32];
|
||||
void (*machine_quirk)(void);
|
||||
char firmware[FW_NAME_SIZE];
|
||||
struct sst_platform_info *pdata;
|
||||
|
||||
};
|
||||
|
||||
/* LPE viewpoint addresses */
|
||||
#define SST_BYT_IRAM_PHY_START 0xff2c0000
|
||||
#define SST_BYT_IRAM_PHY_END 0xff2d4000
|
||||
|
@ -223,37 +214,16 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
|
||||
void *context, void **ret)
|
||||
{
|
||||
*(bool *)context = true;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static struct sst_machines *sst_acpi_find_machine(
|
||||
struct sst_machines *machines)
|
||||
{
|
||||
struct sst_machines *mach;
|
||||
bool found = false;
|
||||
|
||||
for (mach = machines; mach->codec_id; mach++)
|
||||
if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
|
||||
sst_acpi_mach_match,
|
||||
&found, NULL)) && found)
|
||||
return mach;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sst_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret = 0;
|
||||
struct intel_sst_drv *ctx;
|
||||
const struct acpi_device_id *id;
|
||||
struct sst_machines *mach;
|
||||
struct sst_acpi_mach *mach;
|
||||
struct platform_device *mdev;
|
||||
struct platform_device *plat_dev;
|
||||
struct sst_platform_info *pdata;
|
||||
unsigned int dev_id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
|
@ -261,12 +231,13 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
dev_dbg(dev, "for %s", id->id);
|
||||
|
||||
mach = (struct sst_machines *)id->driver_data;
|
||||
mach = (struct sst_acpi_mach *)id->driver_data;
|
||||
mach = sst_acpi_find_machine(mach);
|
||||
if (mach == NULL) {
|
||||
dev_err(dev, "No matching machine driver found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pdata = mach->pdata;
|
||||
|
||||
ret = kstrtouint(id->id, 16, &dev_id);
|
||||
if (ret < 0) {
|
||||
|
@ -276,16 +247,23 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
|||
|
||||
dev_dbg(dev, "ACPI device id: %x\n", dev_id);
|
||||
|
||||
plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
|
||||
plat_dev = platform_device_register_data(dev, pdata->platform, -1,
|
||||
NULL, 0);
|
||||
if (IS_ERR(plat_dev)) {
|
||||
dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
|
||||
dev_err(dev, "Failed to create machine device: %s\n",
|
||||
pdata->platform);
|
||||
return PTR_ERR(plat_dev);
|
||||
}
|
||||
|
||||
/* Create platform device for sst machine driver */
|
||||
mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
|
||||
/*
|
||||
* Create platform device for sst machine driver,
|
||||
* pass machine info as pdata
|
||||
*/
|
||||
mdev = platform_device_register_data(dev, mach->drv_name, -1,
|
||||
(const void *)mach, sizeof(*mach));
|
||||
if (IS_ERR(mdev)) {
|
||||
dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
|
||||
dev_err(dev, "Failed to create machine device: %s\n",
|
||||
mach->drv_name);
|
||||
return PTR_ERR(mdev);
|
||||
}
|
||||
|
||||
|
@ -294,8 +272,8 @@ static int sst_acpi_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
/* Fill sst platform data */
|
||||
ctx->pdata = mach->pdata;
|
||||
strcpy(ctx->firmware_name, mach->firmware);
|
||||
ctx->pdata = pdata;
|
||||
strcpy(ctx->firmware_name, mach->fw_filename);
|
||||
|
||||
ret = sst_platform_get_resources(ctx);
|
||||
if (ret)
|
||||
|
@ -342,22 +320,28 @@ static int sst_acpi_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct sst_machines sst_acpi_bytcr[] = {
|
||||
{"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin",
|
||||
static struct sst_acpi_mach sst_acpi_bytcr[] = {
|
||||
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
|
||||
&byt_rvp_platform_data },
|
||||
{"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
|
||||
&byt_rvp_platform_data },
|
||||
{"INTCCFFD", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
|
||||
&byt_rvp_platform_data },
|
||||
{"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,
|
||||
&byt_rvp_platform_data },
|
||||
{},
|
||||
};
|
||||
|
||||
/* Cherryview-based platforms: CherryTrail and Braswell */
|
||||
static struct sst_machines sst_acpi_chv[] = {
|
||||
{"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin",
|
||||
static struct sst_acpi_mach sst_acpi_chv[] = {
|
||||
{"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
|
||||
{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"10EC5650", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
|
||||
{"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
|
||||
&chv_platform_data },
|
||||
{"193C9890", "cht-bsw", "cht-bsw-max98090", NULL,
|
||||
"intel/fw_sst_22a8.bin", &chv_platform_data },
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
|
|||
str_id, pipe_id);
|
||||
ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
|
||||
IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
|
||||
&alloc_param, data, true, true, false, true);
|
||||
&alloc_param, &data, true, true, false, true);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
|
||||
|
|
|
@ -3,17 +3,23 @@ snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
|
|||
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
|
||||
snd-soc-sst-broadwell-objs := broadwell.o
|
||||
snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
|
||||
snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
|
||||
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
|
||||
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
|
||||
snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
|
||||
snd-soc-skl_rt286-objs := skl_rt286.o
|
||||
snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
|
||||
snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o
|
||||
|
|
|
@ -20,51 +20,76 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include "../../codecs/rt5640.h"
|
||||
#include "../atom/sst-atom-controls.h"
|
||||
#include "../common/sst-acpi.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
|
||||
static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Int Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
SND_SOC_DAPM_MIC("Internal Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_audio_map[] = {
|
||||
{"IN2P", NULL, "Headset Mic"},
|
||||
{"IN2N", NULL, "Headset Mic"},
|
||||
{"Headset Mic", NULL, "MICBIAS1"},
|
||||
{"IN1P", NULL, "MICBIAS1"},
|
||||
{"LDO2", NULL, "Int Mic"},
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"Headphone", NULL, "HPOR"},
|
||||
{"Ext Spk", NULL, "SPOLP"},
|
||||
{"Ext Spk", NULL, "SPOLN"},
|
||||
{"Ext Spk", NULL, "SPORP"},
|
||||
{"Ext Spk", NULL, "SPORN"},
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
|
||||
{"AIF1 Playback", NULL, "ssp2 Tx"},
|
||||
{"ssp2 Tx", NULL, "codec_out0"},
|
||||
{"ssp2 Tx", NULL, "codec_out1"},
|
||||
{"codec_in0", NULL, "ssp2 Rx"},
|
||||
{"codec_in1", NULL, "ssp2 Rx"},
|
||||
{"ssp2 Rx", NULL, "AIF1 Capture"},
|
||||
|
||||
{"Headset Mic", NULL, "MICBIAS1"},
|
||||
{"IN2P", NULL, "Headset Mic"},
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"Headphone", NULL, "HPOR"},
|
||||
{"Speaker", NULL, "SPOLP"},
|
||||
{"Speaker", NULL, "SPOLN"},
|
||||
{"Speaker", NULL, "SPORP"},
|
||||
{"Speaker", NULL, "SPORN"},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new byt_mc_controls[] = {
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
|
||||
{"DMIC1", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
|
||||
{"DMIC2", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
|
||||
{"Internal Mic", NULL, "MICBIAS1"},
|
||||
{"IN1P", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
enum {
|
||||
BYT_RT5640_DMIC1_MAP,
|
||||
BYT_RT5640_DMIC2_MAP,
|
||||
BYT_RT5640_IN1_MAP,
|
||||
};
|
||||
|
||||
#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
|
||||
#define BYT_RT5640_DMIC_EN BIT(16)
|
||||
|
||||
static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
|
||||
BYT_RT5640_DMIC_EN;
|
||||
|
||||
static const struct snd_kcontrol_new byt_rt5640_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Int Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Ext Spk"),
|
||||
SOC_DAPM_PIN_SWITCH("Internal Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
};
|
||||
|
||||
static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
|
||||
static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
@ -92,7 +117,95 @@ static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_pcm_stream byt_dai_params = {
|
||||
static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
byt_rt5640_quirk = (unsigned long)id->driver_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id byt_rt5640_quirk_table[] = {
|
||||
{
|
||||
.callback = byt_rt5640_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
},
|
||||
.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
|
||||
},
|
||||
{
|
||||
.callback = byt_rt5640_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
|
||||
},
|
||||
.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
|
||||
BYT_RT5640_DMIC_EN),
|
||||
},
|
||||
{
|
||||
.callback = byt_rt5640_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
|
||||
},
|
||||
.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = runtime->codec;
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
const struct snd_soc_dapm_route *custom_map;
|
||||
int num_routes;
|
||||
|
||||
card->dapm.idle_bias_off = true;
|
||||
|
||||
rt5640_sel_asrc_clk_src(codec,
|
||||
RT5640_DA_STEREO_FILTER |
|
||||
RT5640_AD_STEREO_FILTER,
|
||||
RT5640_CLK_SEL_ASRC);
|
||||
|
||||
ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
|
||||
ARRAY_SIZE(byt_rt5640_controls));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "unable to add card controls\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dmi_check_system(byt_rt5640_quirk_table);
|
||||
switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
|
||||
case BYT_RT5640_IN1_MAP:
|
||||
custom_map = byt_rt5640_intmic_in1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
|
||||
break;
|
||||
case BYT_RT5640_DMIC2_MAP:
|
||||
custom_map = byt_rt5640_intmic_dmic2_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
|
||||
break;
|
||||
default:
|
||||
custom_map = byt_rt5640_intmic_dmic1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
|
||||
ret = rt5640_dmic_enable(codec, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_pcm_stream byt_rt5640_dai_params = {
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_LE,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
|
@ -100,13 +213,14 @@ static const struct snd_soc_pcm_stream byt_dai_params = {
|
|||
.channels_max = 2,
|
||||
};
|
||||
|
||||
static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
int ret;
|
||||
|
||||
/* The DSP will covert the FE rate to 48k, stereo, 24bits */
|
||||
rate->min = rate->max = 48000;
|
||||
|
@ -114,24 +228,46 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||
|
||||
/* set SSP2 to 24-bit */
|
||||
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
|
||||
|
||||
/*
|
||||
* Default mode for SSP configuration is TDM 4 slot, override config
|
||||
* with explicit setting to I2S 2ch 24-bit. The word length is set with
|
||||
* dai_set_tdm_slot() since there is no other API exposed
|
||||
*/
|
||||
ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
|
||||
SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_IF |
|
||||
SND_SOC_DAIFMT_CBS_CFS
|
||||
);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int byt_aif1_startup(struct snd_pcm_substream *substream)
|
||||
static int byt_rt5640_aif1_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_single(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_RATE, 48000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops byt_aif1_ops = {
|
||||
.startup = byt_aif1_startup,
|
||||
static struct snd_soc_ops byt_rt5640_aif1_ops = {
|
||||
.startup = byt_rt5640_aif1_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_ops byt_be_ssp2_ops = {
|
||||
.hw_params = byt_aif1_hw_params,
|
||||
static struct snd_soc_ops byt_rt5640_be_ssp2_ops = {
|
||||
.hw_params = byt_rt5640_aif1_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link byt_dailink[] = {
|
||||
static struct snd_soc_dai_link byt_rt5640_dais[] = {
|
||||
[MERR_DPCM_AUDIO] = {
|
||||
.name = "Baytrail Audio Port",
|
||||
.stream_name = "Baytrail Audio",
|
||||
|
@ -143,7 +279,20 @@ static struct snd_soc_dai_link byt_dailink[] = {
|
|||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &byt_aif1_ops,
|
||||
.ops = &byt_rt5640_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &byt_rt5640_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Baytrail Compressed Port",
|
||||
|
@ -161,58 +310,69 @@ static struct snd_soc_dai_link byt_dailink[] = {
|
|||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
.codec_dai_name = "rt5640-aif1",
|
||||
.codec_name = "i2c-10EC5640:00",
|
||||
.codec_name = "i2c-10EC5640:00", /* overwritten with HID */
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
|
||||
| SND_SOC_DAIFMT_CBS_CFS,
|
||||
.be_hw_params_fixup = byt_codec_fixup,
|
||||
.be_hw_params_fixup = byt_rt5640_codec_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &byt_be_ssp2_ops,
|
||||
.init = byt_rt5640_init,
|
||||
.ops = &byt_rt5640_be_ssp2_ops,
|
||||
},
|
||||
};
|
||||
|
||||
/* SoC card */
|
||||
static struct snd_soc_card snd_soc_card_byt = {
|
||||
.name = "baytrailcraudio",
|
||||
static struct snd_soc_card byt_rt5640_card = {
|
||||
.name = "bytcr-rt5640",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = byt_dailink,
|
||||
.num_links = ARRAY_SIZE(byt_dailink),
|
||||
.dapm_widgets = byt_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
|
||||
.dapm_routes = byt_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(byt_audio_map),
|
||||
.controls = byt_mc_controls,
|
||||
.num_controls = ARRAY_SIZE(byt_mc_controls),
|
||||
.dai_link = byt_rt5640_dais,
|
||||
.num_links = ARRAY_SIZE(byt_rt5640_dais),
|
||||
.dapm_widgets = byt_rt5640_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
|
||||
.dapm_routes = byt_rt5640_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int snd_byt_mc_probe(struct platform_device *pdev)
|
||||
static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
|
||||
|
||||
static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct sst_acpi_mach *mach;
|
||||
|
||||
/* register the soc card */
|
||||
snd_soc_card_byt.dev = &pdev->dev;
|
||||
byt_rt5640_card.dev = &pdev->dev;
|
||||
mach = byt_rt5640_card.dev->platform_data;
|
||||
|
||||
/* fixup codec name based on HID */
|
||||
snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
|
||||
"%s%s%s", "i2c-", mach->id, ":00");
|
||||
byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name;
|
||||
|
||||
ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
|
||||
|
||||
ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
|
||||
if (ret_val) {
|
||||
dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
|
||||
dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
|
||||
ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
platform_set_drvdata(pdev, &snd_soc_card_byt);
|
||||
platform_set_drvdata(pdev, &byt_rt5640_card);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static struct platform_driver snd_byt_mc_driver = {
|
||||
static struct platform_driver snd_byt_rt5640_mc_driver = {
|
||||
.driver = {
|
||||
.name = "bytt100_rt5640",
|
||||
.name = "bytcr_rt5640",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = snd_byt_mc_probe,
|
||||
.probe = snd_byt_rt5640_mc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(snd_byt_mc_driver);
|
||||
module_platform_driver(snd_byt_rt5640_mc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
|
||||
MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:bytt100_rt5640");
|
||||
MODULE_ALIAS("platform:bytcr_rt5640");
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* bytcr_rt5651.c - ASoc Machine driver for Intel Byt CR platform
|
||||
* (derived from bytcr_rt5640.c)
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corp
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include "../../codecs/rt5651.h"
|
||||
#include "../atom/sst-atom-controls.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Internal Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = {
|
||||
{"AIF1 Playback", NULL, "ssp2 Tx"},
|
||||
{"ssp2 Tx", NULL, "codec_out0"},
|
||||
{"ssp2 Tx", NULL, "codec_out1"},
|
||||
{"codec_in0", NULL, "ssp2 Rx"},
|
||||
{"codec_in1", NULL, "ssp2 Rx"},
|
||||
{"ssp2 Rx", NULL, "AIF1 Capture"},
|
||||
|
||||
{"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */
|
||||
{"IN2P", NULL, "Headset Mic"},
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"Headphone", NULL, "HPOR"},
|
||||
{"Speaker", NULL, "LOUTL"},
|
||||
{"Speaker", NULL, "LOUTR"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic1_map[] = {
|
||||
{"DMIC1", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic2_map[] = {
|
||||
{"DMIC2", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = {
|
||||
{"Internal Mic", NULL, "micbias1"},
|
||||
{"IN1P", NULL, "Internal Mic"},
|
||||
};
|
||||
|
||||
enum {
|
||||
BYT_RT5651_DMIC1_MAP,
|
||||
BYT_RT5651_DMIC2_MAP,
|
||||
BYT_RT5651_IN1_MAP,
|
||||
};
|
||||
|
||||
#define BYT_RT5651_MAP(quirk) ((quirk) & 0xff)
|
||||
#define BYT_RT5651_DMIC_EN BIT(16)
|
||||
|
||||
static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP |
|
||||
BYT_RT5651_DMIC_EN;
|
||||
|
||||
static const struct snd_kcontrol_new byt_rt5651_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Internal Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Speaker"),
|
||||
};
|
||||
|
||||
static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
|
||||
snd_soc_dai_set_bclk_ratio(codec_dai, 50);
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1,
|
||||
params_rate(params) * 512,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set codec clock %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5651_PLL1_S_BCLK1,
|
||||
params_rate(params) * 50,
|
||||
params_rate(params) * 512);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id byt_rt5651_quirk_table[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
const struct snd_soc_dapm_route *custom_map;
|
||||
int num_routes;
|
||||
|
||||
card->dapm.idle_bias_off = true;
|
||||
|
||||
dmi_check_system(byt_rt5651_quirk_table);
|
||||
switch (BYT_RT5651_MAP(byt_rt5651_quirk)) {
|
||||
case BYT_RT5651_IN1_MAP:
|
||||
custom_map = byt_rt5651_intmic_in1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_map);
|
||||
break;
|
||||
case BYT_RT5651_DMIC2_MAP:
|
||||
custom_map = byt_rt5651_intmic_dmic2_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic2_map);
|
||||
break;
|
||||
default:
|
||||
custom_map = byt_rt5651_intmic_dmic1_map;
|
||||
num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic1_map);
|
||||
}
|
||||
|
||||
ret = snd_soc_add_card_controls(card, byt_rt5651_controls,
|
||||
ARRAY_SIZE(byt_rt5651_controls));
|
||||
if (ret) {
|
||||
dev_err(card->dev, "unable to add card controls\n");
|
||||
return ret;
|
||||
}
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_pcm_stream byt_rt5651_dai_params = {
|
||||
.formats = SNDRV_PCM_FMTBIT_S24_LE,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
};
|
||||
|
||||
static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
int ret;
|
||||
|
||||
/* The DSP will covert the FE rate to 48k, stereo, 24bits */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
|
||||
/* set SSP2 to 24-bit */
|
||||
params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
|
||||
|
||||
/*
|
||||
* Default mode for SSP configuration is TDM 4 slot, override config
|
||||
* with explicit setting to I2S 2ch 24-bit. The word length is set with
|
||||
* dai_set_tdm_slot() since there is no other API exposed
|
||||
*/
|
||||
ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
|
||||
SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_IF |
|
||||
SND_SOC_DAIFMT_CBS_CFS
|
||||
);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates_48000[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_48000 = {
|
||||
.count = ARRAY_SIZE(rates_48000),
|
||||
.list = rates_48000,
|
||||
};
|
||||
|
||||
static int byt_rt5651_aif1_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_48000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops byt_rt5651_aif1_ops = {
|
||||
.startup = byt_rt5651_aif1_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_ops byt_rt5651_be_ssp2_ops = {
|
||||
.hw_params = byt_rt5651_aif1_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link byt_rt5651_dais[] = {
|
||||
[MERR_DPCM_AUDIO] = {
|
||||
.name = "Audio Port",
|
||||
.stream_name = "Audio",
|
||||
.cpu_dai_name = "media-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &byt_rt5651_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &byt_rt5651_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
.cpu_dai_name = "compress-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
},
|
||||
/* CODEC<->CODEC link */
|
||||
/* back ends */
|
||||
{
|
||||
.name = "SSP2-Codec",
|
||||
.be_id = 1,
|
||||
.cpu_dai_name = "ssp2-port",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
.codec_dai_name = "rt5651-aif1",
|
||||
.codec_name = "i2c-10EC5651:00",
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
|
||||
| SND_SOC_DAIFMT_CBS_CFS,
|
||||
.be_hw_params_fixup = byt_rt5651_codec_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = true,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.init = byt_rt5651_init,
|
||||
.ops = &byt_rt5651_be_ssp2_ops,
|
||||
},
|
||||
};
|
||||
|
||||
/* SoC card */
|
||||
static struct snd_soc_card byt_rt5651_card = {
|
||||
.name = "bytcr-rt5651",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = byt_rt5651_dais,
|
||||
.num_links = ARRAY_SIZE(byt_rt5651_dais),
|
||||
.dapm_widgets = byt_rt5651_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(byt_rt5651_widgets),
|
||||
.dapm_routes = byt_rt5651_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(byt_rt5651_audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
/* register the soc card */
|
||||
byt_rt5651_card.dev = &pdev->dev;
|
||||
|
||||
ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card);
|
||||
|
||||
if (ret_val) {
|
||||
dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
|
||||
ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
platform_set_drvdata(pdev, &byt_rt5651_card);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static struct platform_driver snd_byt_rt5651_mc_driver = {
|
||||
.driver = {
|
||||
.name = "bytcr_rt5651",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = snd_byt_rt5651_mc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(snd_byt_rt5651_mc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver for RT5651");
|
||||
MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:bytcr_rt5651");
|
|
@ -41,12 +41,9 @@ struct cht_mc_private {
|
|||
|
||||
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
rtd = card->rtd + i;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
|
||||
strlen(CHT_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
|
@ -235,6 +232,18 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
|||
.dpcm_capture = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
|
|
|
@ -47,12 +47,9 @@ struct cht_mc_private {
|
|||
|
||||
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
rtd = card->rtd + i;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
|
||||
strlen(CHT_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
|
@ -263,6 +260,18 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
|||
.dpcm_capture = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
|
|
|
@ -46,12 +46,9 @@ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
|
|||
|
||||
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
rtd = card->rtd + i;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
|
||||
strlen(CHT_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
|
@ -251,6 +248,18 @@ static struct snd_soc_dai_link cht_dailink[] = {
|
|||
.dpcm_capture = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_DEEP_BUFFER] = {
|
||||
.name = "Deep-Buffer Audio Port",
|
||||
.stream_name = "Deep-Buffer Audio",
|
||||
.cpu_dai_name = "deepbuffer-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.nonatomic = true,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
|
|
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
* Intel Skylake I2S Machine Driver with MAXIM98357A
|
||||
* and NAU88L25
|
||||
*
|
||||
* Copyright (C) 2015, Intel Corporation. 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 version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include "../../codecs/nau8825.h"
|
||||
|
||||
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
|
||||
#define SKL_MAXIM_CODEC_DAI "HiFi"
|
||||
|
||||
static struct snd_soc_jack skylake_headset;
|
||||
static struct snd_soc_card skylake_audio_card;
|
||||
|
||||
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI,
|
||||
strlen(SKL_NUVOTON_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
codec_dai = skl_get_codec_dai(card);
|
||||
if (!codec_dai) {
|
||||
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new skylake_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Spk"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget skylake_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Spk", NULL),
|
||||
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
|
||||
SND_SOC_DAPM_SINK("WoV Sink"),
|
||||
SND_SOC_DAPM_SPK("DP", NULL),
|
||||
SND_SOC_DAPM_SPK("HDMI", NULL),
|
||||
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
|
||||
platform_clock_control, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route skylake_map[] = {
|
||||
/* HP jack connectors - unknown if we have jack detection */
|
||||
{ "Headphone Jack", NULL, "HPOL" },
|
||||
{ "Headphone Jack", NULL, "HPOR" },
|
||||
|
||||
/* speaker */
|
||||
{ "Spk", NULL, "Speaker" },
|
||||
|
||||
/* other jacks */
|
||||
{ "MIC", NULL, "Headset Mic" },
|
||||
{ "DMic", NULL, "SoC DMIC" },
|
||||
|
||||
{"WoV Sink", NULL, "hwd_in sink"},
|
||||
{"HDMI", NULL, "hif5 Output"},
|
||||
{"DP", NULL, "hif6 Output"},
|
||||
|
||||
/* CODEC BE connections */
|
||||
{ "HiFi Playback", NULL, "ssp0 Tx" },
|
||||
{ "ssp0 Tx", NULL, "codec0_out" },
|
||||
|
||||
{ "Playback", NULL, "ssp1 Tx" },
|
||||
{ "ssp1 Tx", NULL, "codec1_out" },
|
||||
|
||||
{ "codec0_in", NULL, "ssp1 Rx" },
|
||||
{ "ssp1 Rx", NULL, "Capture" },
|
||||
|
||||
/* DMIC */
|
||||
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
|
||||
{ "DMIC01 Rx", NULL, "DMIC AIF" },
|
||||
{ "hifi1", NULL, "iDisp Tx"},
|
||||
{ "iDisp Tx", NULL, "iDisp_out"},
|
||||
{ "Headphone Jack", NULL, "Platform Clock" },
|
||||
{ "Headset Mic", NULL, "Platform Clock" },
|
||||
};
|
||||
|
||||
static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
|
||||
/* The ADSP will covert the FE rate to 48k, stereo */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
|
||||
/* set SSP0 to 24 bit */
|
||||
snd_mask_none(fmt);
|
||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
|
||||
/*
|
||||
* Headset buttons map to the google Reference headset.
|
||||
* These can be configured by userspace.
|
||||
*/
|
||||
ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nau8825_enable_jack_detect(codec, &skylake_headset);
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
struct snd_soc_component *component = rtd->cpu_dai->component;
|
||||
|
||||
dapm = snd_soc_component_get_dapm(component);
|
||||
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static unsigned int channels[] = {
|
||||
2,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_channels = {
|
||||
.count = ARRAY_SIZE(channels),
|
||||
.list = channels,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skl_fe_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
/*
|
||||
* On this platform for PCM device we support,
|
||||
* 48Khz
|
||||
* stereo
|
||||
* 16 bit audio
|
||||
*/
|
||||
|
||||
runtime->hw.channels_max = 2;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_channels);
|
||||
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
|
||||
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops skylake_nau8825_fe_ops = {
|
||||
.startup = skl_fe_startup,
|
||||
};
|
||||
|
||||
static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_nau8825_ops = {
|
||||
.hw_params = skylake_nau8825_hw_params,
|
||||
};
|
||||
|
||||
static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
|
||||
if (params_channels(params) == 2)
|
||||
channels->min = channels->max = 2;
|
||||
else
|
||||
channels->min = channels->max = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int channels_dmic[] = {
|
||||
2, 4,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
|
||||
.count = ARRAY_SIZE(channels_dmic),
|
||||
.list = channels_dmic,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
runtime->hw.channels_max = 4;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_dmic_channels);
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_dmic_ops = {
|
||||
.startup = skylake_dmic_startup,
|
||||
};
|
||||
|
||||
static unsigned int rates_16000[] = {
|
||||
16000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_16000 = {
|
||||
.count = ARRAY_SIZE(rates_16000),
|
||||
.list = rates_16000,
|
||||
};
|
||||
|
||||
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_16000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylaye_refcap_ops = {
|
||||
.startup = skylake_refcap_startup,
|
||||
};
|
||||
|
||||
/* skylake digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link skylake_dais[] = {
|
||||
/* Front End DAI links */
|
||||
{
|
||||
.name = "Skl Audio Port",
|
||||
.stream_name = "Audio",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.init = skylake_nau8825_fe_init,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_playback = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Capture Port",
|
||||
.stream_name = "Audio Record",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_capture = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Reference cap",
|
||||
.stream_name = "Wake on Voice",
|
||||
.cpu_dai_name = "Reference Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylaye_refcap_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio DMIC cap",
|
||||
.stream_name = "dmiccap",
|
||||
.cpu_dai_name = "DMIC Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylake_dmic_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl HDMI Port",
|
||||
.stream_name = "Hdmi",
|
||||
.cpu_dai_name = "HDMI Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.init = NULL,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
|
||||
/* Back End DAI links */
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "SSP0-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP0 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codec_name = "MX98357A:00",
|
||||
.codec_dai_name = SKL_MAXIM_CODEC_DAI,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.dpcm_playback = 1,
|
||||
},
|
||||
{
|
||||
/* SSP1 - Codec */
|
||||
.name = "SSP1-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP1 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codec_name = "i2c-10508825:00",
|
||||
.codec_dai_name = SKL_NUVOTON_CODEC_DAI,
|
||||
.init = skylake_nau8825_codec_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.ops = &skylake_nau8825_ops,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
},
|
||||
{
|
||||
.name = "dmic01",
|
||||
.be_id = 1,
|
||||
.cpu_dai_name = "DMIC01 Pin",
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.be_hw_params_fixup = skylake_dmic_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.dpcm_capture = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
{
|
||||
.name = "iDisp",
|
||||
.be_id = 3,
|
||||
.cpu_dai_name = "iDisp Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi1",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
};
|
||||
|
||||
/* skylake audio machine driver for SPT + NAU88L25 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825max",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = skylake_dais,
|
||||
.num_links = ARRAY_SIZE(skylake_dais),
|
||||
.controls = skylake_controls,
|
||||
.num_controls = ARRAY_SIZE(skylake_controls),
|
||||
.dapm_widgets = skylake_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
|
||||
.dapm_routes = skylake_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int skylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
skylake_audio_card.dev = &pdev->dev;
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
||||
static struct platform_driver skylake_audio = {
|
||||
.probe = skylake_audio_probe,
|
||||
.driver = {
|
||||
.name = "skl_nau88l25_max98357a_i2s",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(skylake_audio)
|
||||
|
||||
/* Module information */
|
||||
MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode");
|
||||
MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s");
|
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* Intel Skylake I2S Machine Driver for NAU88L25+SSM4567
|
||||
*
|
||||
* Copyright (C) 2015, Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Modified from:
|
||||
* Intel Skylake I2S Machine Driver for NAU88L25 and SSM4567
|
||||
*
|
||||
* Copyright (C) 2015, Intel Corporation. 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 version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "../../codecs/nau8825.h"
|
||||
|
||||
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
|
||||
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
|
||||
|
||||
static struct snd_soc_jack skylake_headset;
|
||||
static struct snd_soc_card skylake_audio_card;
|
||||
|
||||
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI,
|
||||
strlen(SKL_NUVOTON_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new skylake_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Left Speaker"),
|
||||
SOC_DAPM_PIN_SWITCH("Right Speaker"),
|
||||
};
|
||||
|
||||
static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
codec_dai = skl_get_codec_dai(card);
|
||||
if (!codec_dai) {
|
||||
dev_err(card->dev, "Codec dai not found\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (SND_SOC_DAPM_EVENT_ON(event)) {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "set sysclk err = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget skylake_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Left Speaker", NULL),
|
||||
SND_SOC_DAPM_SPK("Right Speaker", NULL),
|
||||
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
|
||||
SND_SOC_DAPM_SINK("WoV Sink"),
|
||||
SND_SOC_DAPM_SPK("DP", NULL),
|
||||
SND_SOC_DAPM_SPK("HDMI", NULL),
|
||||
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
|
||||
platform_clock_control, SND_SOC_DAPM_PRE_PMU |
|
||||
SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route skylake_map[] = {
|
||||
/* HP jack connectors - unknown if we have jack detection */
|
||||
{"Headphone Jack", NULL, "HPOL"},
|
||||
{"Headphone Jack", NULL, "HPOR"},
|
||||
|
||||
/* speaker */
|
||||
{"Left Speaker", NULL, "Left OUT"},
|
||||
{"Right Speaker", NULL, "Right OUT"},
|
||||
|
||||
/* other jacks */
|
||||
{"MIC", NULL, "Headset Mic"},
|
||||
{"DMic", NULL, "SoC DMIC"},
|
||||
|
||||
{"WoV Sink", NULL, "hwd_in sink"},
|
||||
|
||||
{"HDMI", NULL, "hif5 Output"},
|
||||
{"DP", NULL, "hif6 Output"},
|
||||
/* CODEC BE connections */
|
||||
{ "Left Playback", NULL, "ssp0 Tx"},
|
||||
{ "Right Playback", NULL, "ssp0 Tx"},
|
||||
{ "ssp0 Tx", NULL, "codec0_out"},
|
||||
|
||||
{ "Playback", NULL, "ssp1 Tx"},
|
||||
{ "ssp1 Tx", NULL, "codec1_out"},
|
||||
|
||||
{ "codec0_in", NULL, "ssp1 Rx" },
|
||||
{ "ssp1 Rx", NULL, "Capture" },
|
||||
|
||||
/* DMIC */
|
||||
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
|
||||
{ "DMIC01 Rx", NULL, "DMIC AIF" },
|
||||
{ "hifi1", NULL, "iDisp Tx"},
|
||||
{ "iDisp Tx", NULL, "iDisp_out"},
|
||||
{ "Headphone Jack", NULL, "Platform Clock" },
|
||||
{ "Headset Mic", NULL, "Platform Clock" },
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_conf ssm4567_codec_conf[] = {
|
||||
{
|
||||
.dev_name = "i2c-INT343B:00",
|
||||
.name_prefix = "Left",
|
||||
},
|
||||
{
|
||||
.dev_name = "i2c-INT343B:01",
|
||||
.name_prefix = "Right",
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link_component ssm4567_codec_components[] = {
|
||||
{ /* Left */
|
||||
.name = "i2c-INT343B:00",
|
||||
.dai_name = SKL_SSM_CODEC_DAI,
|
||||
},
|
||||
{ /* Right */
|
||||
.name = "i2c-INT343B:01",
|
||||
.dai_name = SKL_SSM_CODEC_DAI,
|
||||
},
|
||||
};
|
||||
|
||||
static int skylake_ssm4567_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Slot 1 for left */
|
||||
ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[0], 0x01, 0x01, 2, 48);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Slot 2 for right */
|
||||
ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x02, 0x02, 2, 48);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
|
||||
/*
|
||||
* 4 buttons here map to the google Reference headset
|
||||
* The use of these buttons can be decided by the user space.
|
||||
*/
|
||||
ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack",
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset,
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nau8825_enable_jack_detect(codec, &skylake_headset);
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
struct snd_soc_component *component = rtd->cpu_dai->component;
|
||||
|
||||
dapm = snd_soc_component_get_dapm(component);
|
||||
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static unsigned int channels[] = {
|
||||
2,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_channels = {
|
||||
.count = ARRAY_SIZE(channels),
|
||||
.list = channels,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skl_fe_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
/*
|
||||
* on this platform for PCM device we support,
|
||||
* 48Khz
|
||||
* stereo
|
||||
* 16 bit audio
|
||||
*/
|
||||
|
||||
runtime->hw.channels_max = 2;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_channels);
|
||||
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
|
||||
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops skylake_nau8825_fe_ops = {
|
||||
.startup = skl_fe_startup,
|
||||
};
|
||||
|
||||
static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
|
||||
/* The ADSP will covert the FE rate to 48k, stereo */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
|
||||
/* set SSP0 to 24 bit */
|
||||
snd_mask_none(fmt);
|
||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
if (params_channels(params) == 2)
|
||||
channels->min = channels->max = 2;
|
||||
else
|
||||
channels->min = channels->max = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_nau8825_ops = {
|
||||
.hw_params = skylake_nau8825_hw_params,
|
||||
};
|
||||
|
||||
static unsigned int channels_dmic[] = {
|
||||
2, 4,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
|
||||
.count = ARRAY_SIZE(channels_dmic),
|
||||
.list = channels_dmic,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
runtime->hw.channels_max = 4;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_dmic_channels);
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_dmic_ops = {
|
||||
.startup = skylake_dmic_startup,
|
||||
};
|
||||
|
||||
static unsigned int rates_16000[] = {
|
||||
16000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_16000 = {
|
||||
.count = ARRAY_SIZE(rates_16000),
|
||||
.list = rates_16000,
|
||||
};
|
||||
|
||||
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_16000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylaye_refcap_ops = {
|
||||
.startup = skylake_refcap_startup,
|
||||
};
|
||||
|
||||
/* skylake digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link skylake_dais[] = {
|
||||
/* Front End DAI links */
|
||||
{
|
||||
.name = "Skl Audio Port",
|
||||
.stream_name = "Audio",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.init = skylake_nau8825_fe_init,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_playback = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Capture Port",
|
||||
.stream_name = "Audio Record",
|
||||
.cpu_dai_name = "System Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.nonatomic = 1,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dpcm_capture = 1,
|
||||
.ops = &skylake_nau8825_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Reference cap",
|
||||
.stream_name = "Wake on Voice",
|
||||
.cpu_dai_name = "Reference Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.ignore_suspend = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylaye_refcap_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio DMIC cap",
|
||||
.stream_name = "dmiccap",
|
||||
.cpu_dai_name = "DMIC Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylake_dmic_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl HDMI Port",
|
||||
.stream_name = "Hdmi",
|
||||
.cpu_dai_name = "HDMI Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.init = NULL,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
|
||||
/* Back End DAI links */
|
||||
{
|
||||
/* SSP0 - Codec */
|
||||
.name = "SSP0-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP0 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codecs = ssm4567_codec_components,
|
||||
.num_codecs = ARRAY_SIZE(ssm4567_codec_components),
|
||||
.dai_fmt = SND_SOC_DAIFMT_DSP_A |
|
||||
SND_SOC_DAIFMT_IB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.init = skylake_ssm4567_codec_init,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.dpcm_playback = 1,
|
||||
},
|
||||
{
|
||||
/* SSP1 - Codec */
|
||||
.name = "SSP1-Codec",
|
||||
.be_id = 0,
|
||||
.cpu_dai_name = "SSP1 Pin",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.no_pcm = 1,
|
||||
.codec_name = "i2c-10508825:00",
|
||||
.codec_dai_name = SKL_NUVOTON_CODEC_DAI,
|
||||
.init = skylake_nau8825_codec_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp_fixup,
|
||||
.ops = &skylake_nau8825_ops,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
},
|
||||
{
|
||||
.name = "dmic01",
|
||||
.be_id = 1,
|
||||
.cpu_dai_name = "DMIC01 Pin",
|
||||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.ignore_suspend = 1,
|
||||
.be_hw_params_fixup = skylake_dmic_fixup,
|
||||
.dpcm_capture = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
{
|
||||
.name = "iDisp",
|
||||
.be_id = 3,
|
||||
.cpu_dai_name = "iDisp Pin",
|
||||
.codec_name = "ehdaudio0D2",
|
||||
.codec_dai_name = "intel-hdmi-hifi1",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.dpcm_playback = 1,
|
||||
.no_pcm = 1,
|
||||
},
|
||||
};
|
||||
|
||||
/* skylake audio machine driver for SPT + NAU88L25 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825adi",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = skylake_dais,
|
||||
.num_links = ARRAY_SIZE(skylake_dais),
|
||||
.controls = skylake_controls,
|
||||
.num_controls = ARRAY_SIZE(skylake_controls),
|
||||
.dapm_widgets = skylake_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
|
||||
.dapm_routes = skylake_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_map),
|
||||
.codec_conf = ssm4567_codec_conf,
|
||||
.num_configs = ARRAY_SIZE(ssm4567_codec_conf),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int skylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
skylake_audio_card.dev = &pdev->dev;
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
||||
static struct platform_driver skylake_audio = {
|
||||
.probe = skylake_audio_probe,
|
||||
.driver = {
|
||||
.name = "skl_nau88l25_ssm4567_i2s",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(skylake_audio)
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>");
|
||||
MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>");
|
||||
MODULE_AUTHOR("Naveen M <naveen.m@intel.com>");
|
||||
MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
|
||||
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s");
|
|
@ -52,6 +52,7 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
|
|||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("DMIC2", NULL),
|
||||
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
|
||||
SND_SOC_DAPM_SINK("WoV Sink"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route skylake_rt286_map[] = {
|
||||
|
@ -67,7 +68,9 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
|
|||
|
||||
/* digital mics */
|
||||
{"DMIC1 Pin", NULL, "DMIC2"},
|
||||
{"DMIC AIF", NULL, "SoC DMIC"},
|
||||
{"DMic", NULL, "SoC DMIC"},
|
||||
|
||||
{"WoV Sink", NULL, "hwd_in sink"},
|
||||
|
||||
/* CODEC BE connections */
|
||||
{ "AIF1 Playback", NULL, "ssp0 Tx"},
|
||||
|
@ -79,13 +82,24 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
|
|||
{ "ssp0 Rx", NULL, "AIF1 Capture" },
|
||||
|
||||
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
|
||||
{ "DMIC01 Rx", NULL, "Capture" },
|
||||
{ "DMIC01 Rx", NULL, "DMIC AIF" },
|
||||
|
||||
{ "hif1", NULL, "iDisp Tx"},
|
||||
{ "iDisp Tx", NULL, "iDisp_out"},
|
||||
|
||||
};
|
||||
|
||||
static int skylake_rt286_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm;
|
||||
struct snd_soc_component *component = rtd->cpu_dai->component;
|
||||
|
||||
dapm = snd_soc_component_get_dapm(component);
|
||||
snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
|
@ -101,9 +115,59 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
|
|||
|
||||
rt286_mic_detect(codec, &skylake_headset);
|
||||
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
|
||||
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static unsigned int channels[] = {
|
||||
2,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_channels = {
|
||||
.count = ARRAY_SIZE(channels),
|
||||
.list = channels,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skl_fe_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
/*
|
||||
* on this platform for PCM device we support,
|
||||
* 48Khz
|
||||
* stereo
|
||||
* 16 bit audio
|
||||
*/
|
||||
|
||||
runtime->hw.channels_max = 2;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_channels);
|
||||
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
|
||||
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops skylake_rt286_fe_ops = {
|
||||
.startup = skl_fe_startup,
|
||||
};
|
||||
|
||||
static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
|
@ -112,12 +176,15 @@ static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
|
||||
/* The output is 48KHz, stereo, 16bits */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
|
||||
|
||||
/* set SSP0 to 24 bit */
|
||||
snd_mask_none(fmt);
|
||||
snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -140,6 +207,42 @@ static struct snd_soc_ops skylake_rt286_ops = {
|
|||
.hw_params = skylake_rt286_hw_params,
|
||||
};
|
||||
|
||||
static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
channels->min = channels->max = 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int channels_dmic[] = {
|
||||
2, 4,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
|
||||
.count = ARRAY_SIZE(channels_dmic),
|
||||
.list = channels_dmic,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
runtime->hw.channels_max = 4;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&constraints_dmic_channels);
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops skylake_dmic_ops = {
|
||||
.startup = skylake_dmic_startup,
|
||||
};
|
||||
|
||||
/* skylake digital audio interface glue - connects codec <--> CPU */
|
||||
static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
||||
/* Front End DAI links */
|
||||
|
@ -152,11 +255,13 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
|||
.dynamic = 1,
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.init = skylake_rt286_fe_init,
|
||||
.trigger = {
|
||||
SND_SOC_DPCM_TRIGGER_POST,
|
||||
SND_SOC_DPCM_TRIGGER_POST
|
||||
},
|
||||
.dpcm_playback = 1,
|
||||
.ops = &skylake_rt286_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Capture Port",
|
||||
|
@ -172,6 +277,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
|||
SND_SOC_DPCM_TRIGGER_POST
|
||||
},
|
||||
.dpcm_capture = 1,
|
||||
.ops = &skylake_rt286_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio Reference cap",
|
||||
|
@ -186,6 +292,19 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
|||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
},
|
||||
{
|
||||
.name = "Skl Audio DMIC cap",
|
||||
.stream_name = "dmiccap",
|
||||
.cpu_dai_name = "DMIC Pin",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.init = NULL,
|
||||
.dpcm_capture = 1,
|
||||
.nonatomic = 1,
|
||||
.dynamic = 1,
|
||||
.ops = &skylake_dmic_ops,
|
||||
},
|
||||
|
||||
/* Back End DAI links */
|
||||
{
|
||||
|
@ -201,7 +320,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
|||
.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ignore_suspend = 1,
|
||||
.ignore_pmdown_time = 1,
|
||||
.be_hw_params_fixup = skylake_ssp0_fixup,
|
||||
.ops = &skylake_rt286_ops,
|
||||
|
@ -215,6 +333,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
|||
.codec_name = "dmic-codec",
|
||||
.codec_dai_name = "dmic-hifi",
|
||||
.platform_name = "0000:00:1f.3",
|
||||
.be_hw_params_fixup = skylake_dmic_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.dpcm_capture = 1,
|
||||
.no_pcm = 1,
|
||||
|
@ -247,6 +366,7 @@ static struct platform_driver skylake_audio = {
|
|||
.probe = skylake_audio_probe,
|
||||
.driver = {
|
||||
.name = "skl_alc286s_i2s",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
snd-soc-sst-dsp-objs := sst-dsp.o
|
||||
snd-soc-sst-acpi-objs := sst-acpi.o
|
||||
ifneq ($(CONFIG_SND_SST_IPC_ACPI),)
|
||||
snd-soc-sst-acpi-objs := sst-match-acpi.o
|
||||
else
|
||||
snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o
|
||||
endif
|
||||
|
||||
snd-soc-sst-ipc-objs := sst-ipc.o
|
||||
|
||||
ifneq ($(CONFIG_DW_DMAC_CORE),)
|
||||
snd-soc-sst-dsp-objs += sst-firmware.o
|
||||
endif
|
||||
snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
|
||||
|
||||
|
|
|
@ -21,21 +21,12 @@
|
|||
#include <linux/platform_device.h>
|
||||
|
||||
#include "sst-dsp.h"
|
||||
#include "sst-acpi.h"
|
||||
|
||||
#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000
|
||||
#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000
|
||||
#define SST_LPT_DSP_DMA_SIZE (1024 - 1)
|
||||
|
||||
/* Descriptor for SST ASoC machine driver */
|
||||
struct sst_acpi_mach {
|
||||
/* ACPI ID for the matching machine driver. Audio codec for instance */
|
||||
const u8 id[ACPI_ID_LEN];
|
||||
/* machine driver name */
|
||||
const char *drv_name;
|
||||
/* firmware file name */
|
||||
const char *fw_filename;
|
||||
};
|
||||
|
||||
/* Descriptor for setting up SST platform data */
|
||||
struct sst_acpi_desc {
|
||||
const char *drv_name;
|
||||
|
@ -88,28 +79,6 @@ static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
|
|||
return;
|
||||
}
|
||||
|
||||
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
|
||||
void *context, void **ret)
|
||||
{
|
||||
*(bool *)context = true;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static struct sst_acpi_mach *sst_acpi_find_machine(
|
||||
struct sst_acpi_mach *machines)
|
||||
{
|
||||
struct sst_acpi_mach *mach;
|
||||
bool found = false;
|
||||
|
||||
for (mach = machines; mach->id[0]; mach++)
|
||||
if (ACPI_SUCCESS(acpi_get_devices(mach->id,
|
||||
sst_acpi_mach_match,
|
||||
&found, NULL)) && found)
|
||||
return mach;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sst_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
@ -211,7 +180,7 @@ static int sst_acpi_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static struct sst_acpi_mach haswell_machines[] = {
|
||||
{ "INT33CA", "haswell-audio", "intel/IntcSST1.bin" },
|
||||
{ "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -229,7 +198,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = {
|
|||
};
|
||||
|
||||
static struct sst_acpi_mach broadwell_machines[] = {
|
||||
{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin" },
|
||||
{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -247,8 +216,8 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = {
|
|||
};
|
||||
|
||||
static struct sst_acpi_mach baytrail_machines[] = {
|
||||
{ "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
|
||||
{ "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
|
||||
{ "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
|
||||
{ "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2013-15, Intel Corporation. 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 version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
|
||||
/* acpi match */
|
||||
struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
|
||||
|
||||
/* Descriptor for SST ASoC machine driver */
|
||||
struct sst_acpi_mach {
|
||||
/* ACPI ID for the matching machine driver. Audio codec for instance */
|
||||
const u8 id[ACPI_ID_LEN];
|
||||
/* machine driver name */
|
||||
const char *drv_name;
|
||||
/* firmware file name */
|
||||
const char *fw_filename;
|
||||
|
||||
/* board name */
|
||||
const char *board;
|
||||
void (*machine_quirk)(void);
|
||||
void *pdata;
|
||||
};
|
|
@ -243,7 +243,7 @@ struct sst_mem_block {
|
|||
u32 size; /* block size */
|
||||
u32 index; /* block index 0..N */
|
||||
enum sst_mem_type type; /* block memory type IRAM/DRAM */
|
||||
struct sst_block_ops *ops; /* block operations, if any */
|
||||
const struct sst_block_ops *ops;/* block operations, if any */
|
||||
|
||||
/* block status */
|
||||
u32 bytes_used; /* bytes in use by modules */
|
||||
|
@ -308,6 +308,8 @@ struct sst_dsp {
|
|||
|
||||
/* SKL data */
|
||||
|
||||
const char *fw_name;
|
||||
|
||||
/* To allocate CL dma buffers */
|
||||
struct skl_dsp_loader_ops dsp_ops;
|
||||
struct skl_dsp_fw_ops fw_ops;
|
||||
|
@ -376,8 +378,8 @@ void sst_block_free_scratch(struct sst_dsp *dsp);
|
|||
|
||||
/* Register the DSPs memory blocks - would be nice to read from ACPI */
|
||||
struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
|
||||
u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
|
||||
void *private);
|
||||
u32 size, enum sst_mem_type type, const struct sst_block_ops *ops,
|
||||
u32 index, void *private);
|
||||
void sst_mem_block_unregister_all(struct sst_dsp *dsp);
|
||||
|
||||
/* Create/Free DMA resources */
|
||||
|
|
|
@ -420,7 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
|
||||
#ifdef CONFIG_DW_DMAC_CORE
|
||||
struct sst_dsp *sst_dsp_new(struct device *dev,
|
||||
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
|
||||
{
|
||||
|
|
|
@ -216,7 +216,7 @@ struct sst_pdata {
|
|||
void *dsp;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
|
||||
#ifdef CONFIG_DW_DMAC_CORE
|
||||
/* Initialization */
|
||||
struct sst_dsp *sst_dsp_new(struct device *dev,
|
||||
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
|
||||
|
|
|
@ -51,8 +51,22 @@ struct sst_dma {
|
|||
|
||||
static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
|
||||
{
|
||||
u32 tmp = 0;
|
||||
int i, m, n;
|
||||
const u8 *src_byte = src;
|
||||
|
||||
m = bytes / 4;
|
||||
n = bytes % 4;
|
||||
|
||||
/* __iowrite32_copy use 32bit size values so divide by 4 */
|
||||
__iowrite32_copy((void *)dest, src, bytes/4);
|
||||
__iowrite32_copy((void *)dest, src, m);
|
||||
|
||||
if (n) {
|
||||
for (i = 0; i < n; i++)
|
||||
tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8);
|
||||
__iowrite32_copy((void *)(dest + m * 4), &tmp, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void sst_dma_transfer_complete(void *arg)
|
||||
|
@ -1014,8 +1028,8 @@ EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
|
|||
|
||||
/* register a DSP memory block for use with FW based modules */
|
||||
struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
|
||||
u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
|
||||
void *private)
|
||||
u32 size, enum sst_mem_type type, const struct sst_block_ops *ops,
|
||||
u32 index, void *private)
|
||||
{
|
||||
struct sst_mem_block *block;
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* sst_match_apci.c - SST (LPE) match for ACPI enumeration.
|
||||
*
|
||||
* Copyright (c) 2013-15, Intel Corporation.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "sst-acpi.h"
|
||||
|
||||
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
|
||||
void *context, void **ret)
|
||||
{
|
||||
*(bool *)context = true;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
|
||||
{
|
||||
struct sst_acpi_mach *mach;
|
||||
bool found = false;
|
||||
|
||||
for (mach = machines; mach->id[0]; mach++)
|
||||
if (ACPI_SUCCESS(acpi_get_devices(mach->id,
|
||||
sst_acpi_mach_match,
|
||||
&found, NULL)) && found)
|
||||
return mach;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
|
|
@ -607,7 +607,7 @@ static int hsw_block_disable(struct sst_mem_block *block)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct sst_block_ops sst_hsw_ops = {
|
||||
static const struct sst_block_ops sst_hsw_ops = {
|
||||
.enable = hsw_block_enable,
|
||||
.disable = hsw_block_disable,
|
||||
};
|
||||
|
|
|
@ -778,7 +778,6 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
|
|||
struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
|
||||
struct sst_generic_ipc *ipc = &hsw->ipc;
|
||||
u32 ipcx, ipcd;
|
||||
int handled;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sst->spinlock, flags);
|
||||
|
@ -790,34 +789,30 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
|
|||
if (ipcx & SST_IPCX_DONE) {
|
||||
|
||||
/* Handle Immediate reply from DSP Core */
|
||||
handled = hsw_process_reply(hsw, ipcx);
|
||||
hsw_process_reply(hsw, ipcx);
|
||||
|
||||
if (handled > 0) {
|
||||
/* clear DONE bit - tell DSP we have completed */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
|
||||
SST_IPCX_DONE, 0);
|
||||
/* clear DONE bit - tell DSP we have completed */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
|
||||
SST_IPCX_DONE, 0);
|
||||
|
||||
/* unmask Done interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_DONE, 0);
|
||||
}
|
||||
/* unmask Done interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_DONE, 0);
|
||||
}
|
||||
|
||||
/* new message from DSP */
|
||||
if (ipcd & SST_IPCD_BUSY) {
|
||||
|
||||
/* Handle Notification and Delayed reply from DSP Core */
|
||||
handled = hsw_process_notification(hsw);
|
||||
hsw_process_notification(hsw);
|
||||
|
||||
/* clear BUSY bit and set DONE bit - accept new messages */
|
||||
if (handled > 0) {
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
|
||||
SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
|
||||
SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
|
||||
|
||||
/* unmask busy interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_BUSY, 0);
|
||||
}
|
||||
/* unmask busy interrupt */
|
||||
sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
|
||||
SST_IMRX_BUSY, 0);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||
|
|
|
@ -96,7 +96,7 @@ int skl_init_dsp(struct skl *skl)
|
|||
}
|
||||
|
||||
ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
|
||||
loader_ops, &skl->skl_sst);
|
||||
skl->fw_name, loader_ops, &skl->skl_sst);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -182,94 +182,6 @@ enum skl_bitdepth skl_get_bit_depth(int params)
|
|||
}
|
||||
}
|
||||
|
||||
static u32 skl_create_channel_map(enum skl_ch_cfg ch_cfg)
|
||||
{
|
||||
u32 config;
|
||||
|
||||
switch (ch_cfg) {
|
||||
case SKL_CH_CFG_MONO:
|
||||
config = (0xFFFFFFF0 | SKL_CHANNEL_LEFT);
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_STEREO:
|
||||
config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_2_1:
|
||||
config = (0xFFFFF000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4)
|
||||
| (SKL_CHANNEL_LFE << 8));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_3_0:
|
||||
config = (0xFFFFF000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_3_1:
|
||||
config = (0xFFFF0000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_LFE << 12));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_QUATRO:
|
||||
config = (0xFFFF0000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4)
|
||||
| (SKL_CHANNEL_LEFT_SURROUND << 8)
|
||||
| (SKL_CHANNEL_RIGHT_SURROUND << 12));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_4_0:
|
||||
config = (0xFFFF0000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_CENTER_SURROUND << 12));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_5_0:
|
||||
config = (0xFFF00000 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_CENTER << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_LEFT_SURROUND << 12)
|
||||
| (SKL_CHANNEL_RIGHT_SURROUND << 16));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_5_1:
|
||||
config = (0xFF000000 | SKL_CHANNEL_CENTER
|
||||
| (SKL_CHANNEL_LEFT << 4)
|
||||
| (SKL_CHANNEL_RIGHT << 8)
|
||||
| (SKL_CHANNEL_LEFT_SURROUND << 12)
|
||||
| (SKL_CHANNEL_RIGHT_SURROUND << 16)
|
||||
| (SKL_CHANNEL_LFE << 20));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_DUAL_MONO:
|
||||
config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_LEFT << 4));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_I2S_DUAL_STEREO_0:
|
||||
config = (0xFFFFFF00 | SKL_CHANNEL_LEFT
|
||||
| (SKL_CHANNEL_RIGHT << 4));
|
||||
break;
|
||||
|
||||
case SKL_CH_CFG_I2S_DUAL_STEREO_1:
|
||||
config = (0xFFFF00FF | (SKL_CHANNEL_LEFT << 8)
|
||||
| (SKL_CHANNEL_RIGHT << 12));
|
||||
break;
|
||||
|
||||
default:
|
||||
config = 0xFFFFFFFF;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each module in DSP expects a base module configuration, which consists of
|
||||
* PCM format information, which we calculate in driver and resource values
|
||||
|
@ -280,7 +192,7 @@ static void skl_set_base_module_format(struct skl_sst *ctx,
|
|||
struct skl_module_cfg *mconfig,
|
||||
struct skl_base_cfg *base_cfg)
|
||||
{
|
||||
struct skl_module_fmt *format = &mconfig->in_fmt;
|
||||
struct skl_module_fmt *format = &mconfig->in_fmt[0];
|
||||
|
||||
base_cfg->audio_fmt.number_of_channels = (u8)format->channels;
|
||||
|
||||
|
@ -293,14 +205,14 @@ static void skl_set_base_module_format(struct skl_sst *ctx,
|
|||
format->bit_depth, format->valid_bit_depth,
|
||||
format->ch_cfg);
|
||||
|
||||
base_cfg->audio_fmt.channel_map = skl_create_channel_map(
|
||||
base_cfg->audio_fmt.ch_cfg);
|
||||
base_cfg->audio_fmt.channel_map = format->ch_map;
|
||||
|
||||
base_cfg->audio_fmt.interleaving = SKL_INTERLEAVING_PER_CHANNEL;
|
||||
base_cfg->audio_fmt.interleaving = format->interleaving_style;
|
||||
|
||||
base_cfg->cps = mconfig->mcps;
|
||||
base_cfg->ibs = mconfig->ibs;
|
||||
base_cfg->obs = mconfig->obs;
|
||||
base_cfg->is_pages = mconfig->mem_pages;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -399,7 +311,7 @@ static void skl_setup_out_format(struct skl_sst *ctx,
|
|||
struct skl_module_cfg *mconfig,
|
||||
struct skl_audio_data_format *out_fmt)
|
||||
{
|
||||
struct skl_module_fmt *format = &mconfig->out_fmt;
|
||||
struct skl_module_fmt *format = &mconfig->out_fmt[0];
|
||||
|
||||
out_fmt->number_of_channels = (u8)format->channels;
|
||||
out_fmt->s_freq = format->s_freq;
|
||||
|
@ -407,8 +319,9 @@ static void skl_setup_out_format(struct skl_sst *ctx,
|
|||
out_fmt->valid_bit_depth = format->valid_bit_depth;
|
||||
out_fmt->ch_cfg = format->ch_cfg;
|
||||
|
||||
out_fmt->channel_map = skl_create_channel_map(out_fmt->ch_cfg);
|
||||
out_fmt->interleaving = SKL_INTERLEAVING_PER_CHANNEL;
|
||||
out_fmt->channel_map = format->ch_map;
|
||||
out_fmt->interleaving = format->interleaving_style;
|
||||
out_fmt->sample_type = format->sample_type;
|
||||
|
||||
dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n",
|
||||
out_fmt->number_of_channels, format->s_freq, format->bit_depth);
|
||||
|
@ -423,7 +336,7 @@ static void skl_set_src_format(struct skl_sst *ctx,
|
|||
struct skl_module_cfg *mconfig,
|
||||
struct skl_src_module_cfg *src_mconfig)
|
||||
{
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt;
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt[0];
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig,
|
||||
(struct skl_base_cfg *)src_mconfig);
|
||||
|
@ -440,7 +353,7 @@ static void skl_set_updown_mixer_format(struct skl_sst *ctx,
|
|||
struct skl_module_cfg *mconfig,
|
||||
struct skl_up_down_mixer_cfg *mixer_mconfig)
|
||||
{
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt;
|
||||
struct skl_module_fmt *fmt = &mconfig->out_fmt[0];
|
||||
int i = 0;
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig,
|
||||
|
@ -475,6 +388,47 @@ static void skl_set_copier_format(struct skl_sst *ctx,
|
|||
skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig);
|
||||
}
|
||||
|
||||
/*
|
||||
* Algo module are DSP pre processing modules. Algo module take base module
|
||||
* configuration and params
|
||||
*/
|
||||
|
||||
static void skl_set_algo_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_algo_cfg *algo_mcfg)
|
||||
{
|
||||
struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)algo_mcfg;
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig, base_cfg);
|
||||
|
||||
if (mconfig->formats_config.caps_size == 0)
|
||||
return;
|
||||
|
||||
memcpy(algo_mcfg->params,
|
||||
mconfig->formats_config.caps,
|
||||
mconfig->formats_config.caps_size);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Mic select module allows selecting one or many input channels, thus
|
||||
* acting as a demux.
|
||||
*
|
||||
* Mic select module take base module configuration and out-format
|
||||
* configuration
|
||||
*/
|
||||
static void skl_set_base_outfmt_format(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig,
|
||||
struct skl_base_outfmt_cfg *base_outfmt_mcfg)
|
||||
{
|
||||
struct skl_audio_data_format *out_fmt = &base_outfmt_mcfg->out_fmt;
|
||||
struct skl_base_cfg *base_cfg =
|
||||
(struct skl_base_cfg *)base_outfmt_mcfg;
|
||||
|
||||
skl_set_base_module_format(ctx, mconfig, base_cfg);
|
||||
skl_setup_out_format(ctx, mconfig, out_fmt);
|
||||
}
|
||||
|
||||
static u16 skl_get_module_param_size(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig)
|
||||
{
|
||||
|
@ -492,6 +446,14 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx,
|
|||
case SKL_MODULE_TYPE_UPDWMIX:
|
||||
return sizeof(struct skl_up_down_mixer_cfg);
|
||||
|
||||
case SKL_MODULE_TYPE_ALGO:
|
||||
param_size = sizeof(struct skl_base_cfg);
|
||||
param_size += mconfig->formats_config.caps_size;
|
||||
return param_size;
|
||||
|
||||
case SKL_MODULE_TYPE_BASE_OUTFMT:
|
||||
return sizeof(struct skl_base_outfmt_cfg);
|
||||
|
||||
default:
|
||||
/*
|
||||
* return only base cfg when no specific module type is
|
||||
|
@ -538,6 +500,14 @@ static int skl_set_module_format(struct skl_sst *ctx,
|
|||
skl_set_updown_mixer_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
|
||||
case SKL_MODULE_TYPE_ALGO:
|
||||
skl_set_algo_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
|
||||
case SKL_MODULE_TYPE_BASE_OUTFMT:
|
||||
skl_set_base_outfmt_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
|
||||
default:
|
||||
skl_set_base_module_format(ctx, module_config, *param_data);
|
||||
break;
|
||||
|
@ -571,10 +541,10 @@ static int skl_get_queue_index(struct skl_module_pin *mpin,
|
|||
* In static, the pin_index is fixed based on module_id and instance id
|
||||
*/
|
||||
static int skl_alloc_queue(struct skl_module_pin *mpin,
|
||||
struct skl_module_inst_id id, int max)
|
||||
struct skl_module_cfg *tgt_cfg, int max)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct skl_module_inst_id id = tgt_cfg->id;
|
||||
/*
|
||||
* if pin in dynamic, find first free pin
|
||||
* otherwise find match module and instance id pin as topology will
|
||||
|
@ -583,16 +553,23 @@ static int skl_alloc_queue(struct skl_module_pin *mpin,
|
|||
*/
|
||||
for (i = 0; i < max; i++) {
|
||||
if (mpin[i].is_dynamic) {
|
||||
if (!mpin[i].in_use) {
|
||||
if (!mpin[i].in_use &&
|
||||
mpin[i].pin_state == SKL_PIN_UNBIND) {
|
||||
|
||||
mpin[i].in_use = true;
|
||||
mpin[i].id.module_id = id.module_id;
|
||||
mpin[i].id.instance_id = id.instance_id;
|
||||
mpin[i].tgt_mcfg = tgt_cfg;
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
if (mpin[i].id.module_id == id.module_id &&
|
||||
mpin[i].id.instance_id == id.instance_id)
|
||||
mpin[i].id.instance_id == id.instance_id &&
|
||||
mpin[i].pin_state == SKL_PIN_UNBIND) {
|
||||
|
||||
mpin[i].tgt_mcfg = tgt_cfg;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -606,6 +583,28 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
|
|||
mpin[q_index].id.module_id = 0;
|
||||
mpin[q_index].id.instance_id = 0;
|
||||
}
|
||||
mpin[q_index].pin_state = SKL_PIN_UNBIND;
|
||||
mpin[q_index].tgt_mcfg = NULL;
|
||||
}
|
||||
|
||||
/* Module state will be set to unint, if all the out pin state is UNBIND */
|
||||
|
||||
static void skl_clear_module_state(struct skl_module_pin *mpin, int max,
|
||||
struct skl_module_cfg *mcfg)
|
||||
{
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
if (mpin[i].pin_state == SKL_PIN_UNBIND)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
mcfg->m_state = SKL_MODULE_UNINIT;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -615,7 +614,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
|
|||
* invoke the DSP by sending IPC INIT_INSTANCE using ipc helper
|
||||
*/
|
||||
int skl_init_module(struct skl_sst *ctx,
|
||||
struct skl_module_cfg *mconfig, char *param)
|
||||
struct skl_module_cfg *mconfig)
|
||||
{
|
||||
u16 module_config_size = 0;
|
||||
void *param_data = NULL;
|
||||
|
@ -682,37 +681,30 @@ int skl_unbind_modules(struct skl_sst *ctx,
|
|||
struct skl_module_inst_id dst_id = dst_mcfg->id;
|
||||
int in_max = dst_mcfg->max_in_queue;
|
||||
int out_max = src_mcfg->max_out_queue;
|
||||
int src_index, dst_index;
|
||||
int src_index, dst_index, src_pin_state, dst_pin_state;
|
||||
|
||||
skl_dump_bind_info(ctx, src_mcfg, dst_mcfg);
|
||||
|
||||
if (src_mcfg->m_state != SKL_MODULE_BIND_DONE)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* if intra module unbind, check if both modules are BIND,
|
||||
* then send unbind
|
||||
*/
|
||||
if ((src_mcfg->pipe->ppl_id != dst_mcfg->pipe->ppl_id) &&
|
||||
dst_mcfg->m_state != SKL_MODULE_BIND_DONE)
|
||||
return 0;
|
||||
else if (src_mcfg->m_state < SKL_MODULE_INIT_DONE &&
|
||||
dst_mcfg->m_state < SKL_MODULE_INIT_DONE)
|
||||
return 0;
|
||||
|
||||
/* get src queue index */
|
||||
src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max);
|
||||
if (src_index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index;
|
||||
msg.src_queue = src_index;
|
||||
|
||||
/* get dst queue index */
|
||||
dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max);
|
||||
if (dst_index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index;
|
||||
msg.dst_queue = dst_index;
|
||||
|
||||
src_pin_state = src_mcfg->m_out_pin[src_index].pin_state;
|
||||
dst_pin_state = dst_mcfg->m_in_pin[dst_index].pin_state;
|
||||
|
||||
if (src_pin_state != SKL_PIN_BIND_DONE ||
|
||||
dst_pin_state != SKL_PIN_BIND_DONE)
|
||||
return 0;
|
||||
|
||||
msg.module_id = src_mcfg->id.module_id;
|
||||
msg.instance_id = src_mcfg->id.instance_id;
|
||||
|
@ -722,10 +714,15 @@ int skl_unbind_modules(struct skl_sst *ctx,
|
|||
|
||||
ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
|
||||
if (!ret) {
|
||||
src_mcfg->m_state = SKL_MODULE_UNINIT;
|
||||
/* free queue only if unbind is success */
|
||||
skl_free_queue(src_mcfg->m_out_pin, src_index);
|
||||
skl_free_queue(dst_mcfg->m_in_pin, dst_index);
|
||||
|
||||
/*
|
||||
* check only if src module bind state, bind is
|
||||
* always from src -> sink
|
||||
*/
|
||||
skl_clear_module_state(src_mcfg->m_out_pin, out_max, src_mcfg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -744,8 +741,6 @@ int skl_bind_modules(struct skl_sst *ctx,
|
|||
{
|
||||
int ret;
|
||||
struct skl_ipc_bind_unbind_msg msg;
|
||||
struct skl_module_inst_id src_id = src_mcfg->id;
|
||||
struct skl_module_inst_id dst_id = dst_mcfg->id;
|
||||
int in_max = dst_mcfg->max_in_queue;
|
||||
int out_max = src_mcfg->max_out_queue;
|
||||
int src_index, dst_index;
|
||||
|
@ -756,18 +751,18 @@ int skl_bind_modules(struct skl_sst *ctx,
|
|||
dst_mcfg->m_state < SKL_MODULE_INIT_DONE)
|
||||
return 0;
|
||||
|
||||
src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_id, out_max);
|
||||
src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_mcfg, out_max);
|
||||
if (src_index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index;
|
||||
dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_id, in_max);
|
||||
msg.src_queue = src_index;
|
||||
dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_mcfg, in_max);
|
||||
if (dst_index < 0) {
|
||||
skl_free_queue(src_mcfg->m_out_pin, src_index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index;
|
||||
msg.dst_queue = dst_index;
|
||||
|
||||
dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n",
|
||||
msg.src_queue, msg.dst_queue);
|
||||
|
@ -782,6 +777,8 @@ int skl_bind_modules(struct skl_sst *ctx,
|
|||
|
||||
if (!ret) {
|
||||
src_mcfg->m_state = SKL_MODULE_BIND_DONE;
|
||||
src_mcfg->m_out_pin[src_index].pin_state = SKL_PIN_BIND_DONE;
|
||||
dst_mcfg->m_in_pin[dst_index].pin_state = SKL_PIN_BIND_DONE;
|
||||
} else {
|
||||
/* error case , if IPC fails, clear the queue index */
|
||||
skl_free_queue(src_mcfg->m_out_pin, src_index);
|
||||
|
@ -852,6 +849,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
|
|||
ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "Failed to delete pipeline\n");
|
||||
|
||||
pipe->state = SKL_PIPE_INVALID;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -916,3 +915,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Algo parameter set helper function */
|
||||
int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg)
|
||||
{
|
||||
struct skl_ipc_large_config_msg msg;
|
||||
|
||||
msg.module_id = mcfg->id.module_id;
|
||||
msg.instance_id = mcfg->id.instance_id;
|
||||
msg.param_data_size = size;
|
||||
msg.large_param_id = param_id;
|
||||
|
||||
return skl_ipc_set_large_config(&ctx->ipc, &msg, params);
|
||||
}
|
||||
|
||||
int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg)
|
||||
{
|
||||
struct skl_ipc_large_config_msg msg;
|
||||
|
||||
msg.module_id = mcfg->id.module_id;
|
||||
msg.instance_id = mcfg->id.instance_id;
|
||||
msg.param_data_size = size;
|
||||
msg.large_param_id = param_id;
|
||||
|
||||
return skl_ipc_get_large_config(&ctx->ipc, &msg, params);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ void skl_nhlt_free(void *addr)
|
|||
|
||||
static struct nhlt_specific_cfg *skl_get_specific_cfg(
|
||||
struct device *dev, struct nhlt_fmt *fmt,
|
||||
u8 no_ch, u32 rate, u16 bps)
|
||||
u8 no_ch, u32 rate, u16 bps, u8 linktype)
|
||||
{
|
||||
struct nhlt_specific_cfg *sp_config;
|
||||
struct wav_fmt *wfmt;
|
||||
|
@ -68,11 +68,17 @@ static struct nhlt_specific_cfg *skl_get_specific_cfg(
|
|||
wfmt = &fmt_config->fmt_ext.fmt;
|
||||
dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels,
|
||||
wfmt->bits_per_sample, wfmt->samples_per_sec);
|
||||
if (wfmt->channels == no_ch && wfmt->samples_per_sec == rate &&
|
||||
wfmt->bits_per_sample == bps) {
|
||||
if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) {
|
||||
/*
|
||||
* if link type is dmic ignore rate check as the blob is
|
||||
* generic for all rates
|
||||
*/
|
||||
sp_config = &fmt_config->config;
|
||||
if (linktype == NHLT_LINK_DMIC)
|
||||
return sp_config;
|
||||
|
||||
return sp_config;
|
||||
if (wfmt->samples_per_sec == rate)
|
||||
return sp_config;
|
||||
}
|
||||
|
||||
fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps +
|
||||
|
@ -115,7 +121,7 @@ struct nhlt_specific_cfg
|
|||
struct device *dev = bus->dev;
|
||||
struct nhlt_specific_cfg *sp_config;
|
||||
struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
|
||||
u16 bps = num_ch * s_fmt;
|
||||
u16 bps = (s_fmt == 16) ? 16 : 32;
|
||||
u8 j;
|
||||
|
||||
dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps);
|
||||
|
@ -128,7 +134,8 @@ struct nhlt_specific_cfg
|
|||
if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) {
|
||||
fmt = (struct nhlt_fmt *)(epnt->config.caps +
|
||||
epnt->config.size);
|
||||
sp_config = skl_get_specific_cfg(dev, fmt, num_ch, s_rate, bps);
|
||||
sp_config = skl_get_specific_cfg(dev, fmt, num_ch,
|
||||
s_rate, bps, link_type);
|
||||
if (sp_config)
|
||||
return sp_config;
|
||||
}
|
||||
|
|
|
@ -25,9 +25,12 @@
|
|||
#include <sound/soc.h>
|
||||
#include "skl.h"
|
||||
#include "skl-topology.h"
|
||||
#include "skl-sst-dsp.h"
|
||||
#include "skl-sst-ipc.h"
|
||||
|
||||
#define HDA_MONO 1
|
||||
#define HDA_STEREO 2
|
||||
#define HDA_QUAD 4
|
||||
|
||||
static struct snd_pcm_hardware azx_pcm_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
|
@ -35,16 +38,20 @@ static struct snd_pcm_hardware azx_pcm_hw = {
|
|||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
|
||||
SNDRV_PCM_INFO_HAS_LINK_ATIME |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_8000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = HDA_QUAD,
|
||||
.buffer_bytes_max = AZX_MAX_BUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = AZX_MAX_BUF_SIZE / 2,
|
||||
|
@ -105,6 +112,31 @@ static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *e
|
|||
return HDAC_EXT_STREAM_TYPE_COUPLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if the stream opened is marked as ignore_suspend by machine, if so
|
||||
* then enable suspend_active refcount
|
||||
*
|
||||
* The count supend_active does not need lock as it is used in open/close
|
||||
* and suspend context
|
||||
*/
|
||||
static void skl_set_suspend_active(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai, bool enable)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
|
||||
struct snd_soc_dapm_widget *w;
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
w = dai->playback_widget;
|
||||
else
|
||||
w = dai->capture_widget;
|
||||
|
||||
if (w->ignore_suspend && enable)
|
||||
skl->supend_active++;
|
||||
else if (w->ignore_suspend && !enable)
|
||||
skl->supend_active--;
|
||||
}
|
||||
|
||||
static int skl_pcm_open(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
|
@ -112,12 +144,8 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
|
|||
struct hdac_ext_stream *stream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct skl_dma_params *dma_params;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
|
||||
ret = pm_runtime_get_sync(dai->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
stream = snd_hdac_ext_stream_assign(ebus, substream,
|
||||
skl_get_host_stream_type(ebus));
|
||||
|
@ -146,6 +174,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
|
|||
|
||||
dev_dbg(dai->dev, "stream tag set in dma params=%d\n",
|
||||
dma_params->stream_tag);
|
||||
skl_set_suspend_active(substream, dai, true);
|
||||
snd_pcm_set_sync(substream);
|
||||
|
||||
return 0;
|
||||
|
@ -185,10 +214,6 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream,
|
|||
int err;
|
||||
|
||||
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
|
||||
if (hdac_stream(stream)->prepared) {
|
||||
dev_dbg(dai->dev, "already stream is prepared - returning\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
format_val = skl_get_format(substream, dai);
|
||||
dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
|
||||
|
@ -250,6 +275,7 @@ static void skl_pcm_close(struct snd_pcm_substream *substream,
|
|||
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
|
||||
struct skl_dma_params *dma_params = NULL;
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
|
||||
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
|
||||
|
||||
|
@ -261,9 +287,18 @@ static void skl_pcm_close(struct snd_pcm_substream *substream,
|
|||
* dma_params
|
||||
*/
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
skl_set_suspend_active(substream, dai, false);
|
||||
|
||||
/*
|
||||
* check if close is for "Reference Pin" and set back the
|
||||
* CGCTL.MISCBDCGE if disabled by driver
|
||||
*/
|
||||
if (!strncmp(dai->name, "Reference Pin", 13) &&
|
||||
skl->skl_sst->miscbdcg_disabled) {
|
||||
skl->skl_sst->enable_miscbdcge(dai->dev, true);
|
||||
skl->skl_sst->miscbdcg_disabled = false;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dai->dev);
|
||||
pm_runtime_put_autosuspend(dai->dev);
|
||||
kfree(dma_params);
|
||||
}
|
||||
|
||||
|
@ -291,7 +326,53 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream,
|
|||
p_params.ch = params_channels(params);
|
||||
p_params.s_freq = params_rate(params);
|
||||
p_params.stream = substream->stream;
|
||||
skl_tplg_be_update_params(dai, &p_params);
|
||||
|
||||
return skl_tplg_be_update_params(dai, &p_params);
|
||||
}
|
||||
|
||||
static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct hdac_ext_stream *stream;
|
||||
int start;
|
||||
unsigned long cookie;
|
||||
struct hdac_stream *hstr;
|
||||
|
||||
stream = get_hdac_ext_stream(substream);
|
||||
hstr = hdac_stream(stream);
|
||||
|
||||
if (!hstr->prepared)
|
||||
return -EPIPE;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
start = 1;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
start = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&bus->reg_lock, cookie);
|
||||
|
||||
if (start) {
|
||||
snd_hdac_stream_start(hdac_stream(stream), true);
|
||||
snd_hdac_stream_timecounter_init(hstr, 0);
|
||||
} else {
|
||||
snd_hdac_stream_stop(hdac_stream(stream));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&bus->reg_lock, cookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -302,23 +383,72 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
struct skl *skl = get_skl_ctx(dai->dev);
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
struct skl_module_cfg *mconfig;
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
|
||||
int ret;
|
||||
|
||||
mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
|
||||
if (!mconfig)
|
||||
return -EIO;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
skl_pcm_prepare(substream, dai);
|
||||
/*
|
||||
* enable DMA Resume enable bit for the stream, set the dpib
|
||||
* & lpib position to resune before starting the DMA
|
||||
*/
|
||||
snd_hdac_ext_stream_drsm_enable(ebus, true,
|
||||
hdac_stream(stream)->index);
|
||||
snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib);
|
||||
snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
|
||||
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
/*
|
||||
* Start HOST DMA and Start FE Pipe.This is to make sure that
|
||||
* there are no underrun/overrun in the case when the FE
|
||||
* pipeline is started but there is a delay in starting the
|
||||
* DMA channel on the host.
|
||||
*/
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, true);
|
||||
ret = skl_decoupled_trigger(substream, cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return skl_run_pipe(ctx, mconfig->pipe);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
return skl_stop_pipe(ctx, mconfig->pipe);
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
/*
|
||||
* Stop FE Pipe first and stop DMA. This is to make sure that
|
||||
* there are no underrun/overrun in the case if there is a delay
|
||||
* between the two operations.
|
||||
*/
|
||||
ret = skl_stop_pipe(ctx, mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_decoupled_trigger(substream, cmd);
|
||||
if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
|
||||
/* save the dpib and lpib positions */
|
||||
stream->dpib = readl(ebus->bus.remap_addr +
|
||||
AZX_REG_VS_SDXDPIB_XBASE +
|
||||
(AZX_REG_VS_SDXDPIB_XINTERVAL *
|
||||
hdac_stream(stream)->index));
|
||||
|
||||
stream->lpib = snd_hdac_stream_get_pos_lpib(
|
||||
hdac_stream(stream));
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, false);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_link_hw_params(struct snd_pcm_substream *substream,
|
||||
|
@ -352,9 +482,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
|
|||
p_params.stream = substream->stream;
|
||||
p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
|
||||
|
||||
skl_tplg_be_update_params(dai, &p_params);
|
||||
|
||||
return 0;
|
||||
return skl_tplg_be_update_params(dai, &p_params);
|
||||
}
|
||||
|
||||
static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
|
@ -369,11 +497,6 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct hdac_ext_link *link;
|
||||
|
||||
if (link_dev->link_prepared) {
|
||||
dev_dbg(dai->dev, "already stream is prepared - returning\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dma_params = (struct skl_dma_params *)
|
||||
snd_soc_dai_get_dma_data(codec_dai, substream);
|
||||
if (dma_params)
|
||||
|
@ -381,14 +504,15 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
|
|||
dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n",
|
||||
hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name);
|
||||
|
||||
snd_hdac_ext_link_stream_reset(link_dev);
|
||||
|
||||
snd_hdac_ext_link_stream_setup(link_dev, format_val);
|
||||
|
||||
link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
|
||||
if (!link)
|
||||
return -EINVAL;
|
||||
|
||||
snd_hdac_ext_bus_link_power_up(link);
|
||||
snd_hdac_ext_link_stream_reset(link_dev);
|
||||
|
||||
snd_hdac_ext_link_stream_setup(link_dev, format_val);
|
||||
|
||||
snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
|
||||
link_dev->link_prepared = 1;
|
||||
|
||||
|
@ -400,12 +524,16 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct hdac_ext_stream *link_dev =
|
||||
snd_soc_dai_get_dma_data(dai, substream);
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
|
||||
|
||||
dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
skl_link_pcm_prepare(substream, dai);
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, true);
|
||||
snd_hdac_ext_link_stream_start(link_dev);
|
||||
break;
|
||||
|
||||
|
@ -413,6 +541,8 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream,
|
|||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_hdac_ext_link_stream_clear(link_dev);
|
||||
if (cmd == SNDRV_PCM_TRIGGER_SUSPEND)
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -443,19 +573,6 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int skl_be_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
return pm_runtime_get_sync(dai->dev);
|
||||
}
|
||||
|
||||
static void skl_be_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
pm_runtime_mark_last_busy(dai->dev);
|
||||
pm_runtime_put_autosuspend(dai->dev);
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops skl_pcm_dai_ops = {
|
||||
.startup = skl_pcm_open,
|
||||
.shutdown = skl_pcm_close,
|
||||
|
@ -466,24 +583,18 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = {
|
|||
};
|
||||
|
||||
static struct snd_soc_dai_ops skl_dmic_dai_ops = {
|
||||
.startup = skl_be_startup,
|
||||
.hw_params = skl_be_hw_params,
|
||||
.shutdown = skl_be_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
|
||||
.startup = skl_be_startup,
|
||||
.hw_params = skl_be_hw_params,
|
||||
.shutdown = skl_be_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops skl_link_dai_ops = {
|
||||
.startup = skl_be_startup,
|
||||
.prepare = skl_link_pcm_prepare,
|
||||
.hw_params = skl_link_hw_params,
|
||||
.hw_free = skl_link_hw_free,
|
||||
.trigger = skl_link_pcm_trigger,
|
||||
.shutdown = skl_be_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver skl_platform_dai[] = {
|
||||
|
@ -511,7 +622,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.capture = {
|
||||
.stream_name = "Reference Capture",
|
||||
.channels_min = HDA_MONO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = HDA_QUAD,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
|
@ -538,6 +649,18 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "DMIC Pin",
|
||||
.ops = &skl_pcm_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "DMIC Capture",
|
||||
.channels_min = HDA_MONO,
|
||||
.channels_max = HDA_QUAD,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
},
|
||||
|
||||
/* BE CPU Dais */
|
||||
{
|
||||
.name = "SSP0 Pin",
|
||||
|
@ -557,6 +680,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "SSP1 Pin",
|
||||
.ops = &skl_be_ssp_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "ssp1 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ssp1 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "iDisp Pin",
|
||||
.ops = &skl_link_dai_ops,
|
||||
|
@ -573,8 +714,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.ops = &skl_dmic_dai_ops,
|
||||
.capture = {
|
||||
.stream_name = "DMIC01 Rx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_min = HDA_MONO,
|
||||
.channels_max = HDA_QUAD,
|
||||
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
|
||||
},
|
||||
|
@ -688,66 +829,15 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct hdac_ext_stream *stream;
|
||||
int start;
|
||||
unsigned long cookie;
|
||||
struct hdac_stream *hstr;
|
||||
|
||||
dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name);
|
||||
|
||||
stream = get_hdac_ext_stream(substream);
|
||||
hstr = hdac_stream(stream);
|
||||
|
||||
if (!hstr->prepared)
|
||||
return -EPIPE;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
start = 1;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
start = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&bus->reg_lock, cookie);
|
||||
|
||||
if (start)
|
||||
snd_hdac_stream_start(hdac_stream(stream), true);
|
||||
else
|
||||
snd_hdac_stream_stop(hdac_stream(stream));
|
||||
|
||||
if (start)
|
||||
snd_hdac_stream_timecounter_init(hstr, 0);
|
||||
|
||||
spin_unlock_irqrestore(&bus->reg_lock, cookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
|
||||
if (ebus->ppcap)
|
||||
return skl_decoupled_trigger(substream, cmd);
|
||||
else
|
||||
if (!ebus->ppcap)
|
||||
return skl_coupled_trigger(substream, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* calculate runtime delay from LPIB */
|
||||
|
@ -789,7 +879,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
|
|||
{
|
||||
struct hdac_stream *hstr = hdac_stream(hstream);
|
||||
struct snd_pcm_substream *substream = hstr->substream;
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
struct hdac_ext_bus *ebus;
|
||||
unsigned int pos;
|
||||
int delay;
|
||||
|
||||
|
@ -800,6 +890,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
|
|||
pos = 0;
|
||||
|
||||
if (substream->runtime) {
|
||||
ebus = get_bus_ctx(substream);
|
||||
delay = skl_get_delay_from_lpib(ebus, hstream, pos)
|
||||
+ codec_delay;
|
||||
substream->runtime->delay += delay;
|
||||
|
@ -941,7 +1032,6 @@ int skl_platform_register(struct device *dev)
|
|||
struct skl *skl = ebus_to_skl(ebus);
|
||||
|
||||
INIT_LIST_HEAD(&skl->ppl_list);
|
||||
INIT_LIST_HEAD(&skl->dapm_path_list);
|
||||
|
||||
ret = snd_soc_register_platform(dev, &skl_platform_drv);
|
||||
if (ret) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
|
||||
|
@ -33,6 +34,53 @@ void skl_cldma_int_disable(struct sst_dsp *ctx)
|
|||
SKL_ADSP_REG_ADSPIC, SKL_ADSPIC_CL_DMA, 0);
|
||||
}
|
||||
|
||||
static void skl_cldma_stream_run(struct sst_dsp *ctx, bool enable)
|
||||
{
|
||||
unsigned char val;
|
||||
int timeout;
|
||||
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(enable));
|
||||
|
||||
udelay(3);
|
||||
timeout = 300;
|
||||
do {
|
||||
/* waiting for hardware to report that the stream Run bit set */
|
||||
val = sst_dsp_shim_read(ctx, SKL_ADSP_REG_CL_SD_CTL) &
|
||||
CL_SD_CTL_RUN_MASK;
|
||||
if (enable && val)
|
||||
break;
|
||||
else if (!enable && !val)
|
||||
break;
|
||||
udelay(3);
|
||||
} while (--timeout);
|
||||
|
||||
if (timeout == 0)
|
||||
dev_err(ctx->dev, "Failed to set Run bit=%d enable=%d\n", val, enable);
|
||||
}
|
||||
|
||||
static void skl_cldma_stream_clear(struct sst_dsp *ctx)
|
||||
{
|
||||
/* make sure Run bit is cleared before setting stream register */
|
||||
skl_cldma_stream_run(ctx, 0);
|
||||
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0));
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0));
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0);
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0);
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0);
|
||||
}
|
||||
|
||||
/* Code loader helper APIs */
|
||||
static void skl_cldma_setup_bdle(struct sst_dsp *ctx,
|
||||
struct snd_dma_buffer *dmab_data,
|
||||
|
@ -68,6 +116,7 @@ static void skl_cldma_setup_controller(struct sst_dsp *ctx,
|
|||
struct snd_dma_buffer *dmab_bdl, unsigned int max_size,
|
||||
u32 count)
|
||||
{
|
||||
skl_cldma_stream_clear(ctx);
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL,
|
||||
CL_SD_BDLPLBA(dmab_bdl->addr));
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU,
|
||||
|
@ -107,36 +156,13 @@ static void skl_cldma_cleanup_spb(struct sst_dsp *ctx)
|
|||
sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, 0);
|
||||
}
|
||||
|
||||
static void skl_cldma_trigger(struct sst_dsp *ctx, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(1));
|
||||
else
|
||||
sst_dsp_shim_update_bits_unlocked(ctx,
|
||||
SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(0));
|
||||
}
|
||||
|
||||
static void skl_cldma_cleanup(struct sst_dsp *ctx)
|
||||
{
|
||||
skl_cldma_cleanup_spb(ctx);
|
||||
skl_cldma_stream_clear(ctx);
|
||||
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0));
|
||||
sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL,
|
||||
CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0));
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0));
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0);
|
||||
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0);
|
||||
sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0);
|
||||
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
|
||||
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_bdl);
|
||||
}
|
||||
|
||||
static int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
|
||||
|
@ -164,7 +190,7 @@ cleanup:
|
|||
|
||||
static void skl_cldma_stop(struct sst_dsp *ctx)
|
||||
{
|
||||
ctx->cl_dev.ops.cl_trigger(ctx, false);
|
||||
skl_cldma_stream_run(ctx, false);
|
||||
}
|
||||
|
||||
static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
|
||||
|
@ -175,6 +201,21 @@ static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
|
|||
ctx->cl_dev.dma_buffer_offset, trigger);
|
||||
dev_dbg(ctx->dev, "spib position: %d\n", ctx->cl_dev.curr_spib_pos);
|
||||
|
||||
/*
|
||||
* Check if the size exceeds buffer boundary. If it exceeds
|
||||
* max_buffer size, then copy till buffer size and then copy
|
||||
* remaining buffer from the start of ring buffer.
|
||||
*/
|
||||
if (ctx->cl_dev.dma_buffer_offset + size > ctx->cl_dev.bufsize) {
|
||||
unsigned int size_b = ctx->cl_dev.bufsize -
|
||||
ctx->cl_dev.dma_buffer_offset;
|
||||
memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset,
|
||||
curr_pos, size_b);
|
||||
size -= size_b;
|
||||
curr_pos += size_b;
|
||||
ctx->cl_dev.dma_buffer_offset = 0;
|
||||
}
|
||||
|
||||
memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset,
|
||||
curr_pos, size);
|
||||
|
||||
|
@ -291,7 +332,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx)
|
|||
ctx->cl_dev.ops.cl_setup_controller = skl_cldma_setup_controller;
|
||||
ctx->cl_dev.ops.cl_setup_spb = skl_cldma_setup_spb;
|
||||
ctx->cl_dev.ops.cl_cleanup_spb = skl_cldma_cleanup_spb;
|
||||
ctx->cl_dev.ops.cl_trigger = skl_cldma_trigger;
|
||||
ctx->cl_dev.ops.cl_trigger = skl_cldma_stream_run;
|
||||
ctx->cl_dev.ops.cl_cleanup_controller = skl_cldma_cleanup;
|
||||
ctx->cl_dev.ops.cl_copy_to_dmabuf = skl_cldma_copy_to_buf;
|
||||
ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop;
|
||||
|
|
|
@ -58,9 +58,9 @@ struct sst_dsp_device;
|
|||
|
||||
#define SKL_ADSP_MMIO_LEN 0x10000
|
||||
|
||||
#define SKL_ADSP_W0_STAT_SZ 0x800
|
||||
#define SKL_ADSP_W0_STAT_SZ 0x1000
|
||||
|
||||
#define SKL_ADSP_W0_UP_SZ 0x800
|
||||
#define SKL_ADSP_W0_UP_SZ 0x1000
|
||||
|
||||
#define SKL_ADSP_W1_SZ 0x1000
|
||||
|
||||
|
@ -114,6 +114,9 @@ struct skl_dsp_fw_ops {
|
|||
int (*set_state_D0)(struct sst_dsp *ctx);
|
||||
int (*set_state_D3)(struct sst_dsp *ctx);
|
||||
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
|
||||
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name);
|
||||
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
|
||||
|
||||
};
|
||||
|
||||
struct skl_dsp_loader_ops {
|
||||
|
@ -123,6 +126,17 @@ struct skl_dsp_loader_ops {
|
|||
struct snd_dma_buffer *dmab);
|
||||
};
|
||||
|
||||
struct skl_load_module_info {
|
||||
u16 mod_id;
|
||||
const struct firmware *fw;
|
||||
};
|
||||
|
||||
struct skl_module_table {
|
||||
struct skl_load_module_info *mod_info;
|
||||
unsigned int usage_cnt;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
void skl_cldma_process_intr(struct sst_dsp *ctx);
|
||||
void skl_cldma_int_disable(struct sst_dsp *ctx);
|
||||
int skl_cldma_prepare(struct sst_dsp *ctx);
|
||||
|
@ -139,7 +153,8 @@ void skl_dsp_free(struct sst_dsp *dsp);
|
|||
|
||||
int skl_dsp_boot(struct sst_dsp *ctx);
|
||||
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
||||
struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp);
|
||||
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
|
||||
struct skl_sst **dsp);
|
||||
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
|
||||
|
||||
#endif /*__SKL_SST_DSP_H__*/
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "skl.h"
|
||||
#include "skl-sst-dsp.h"
|
||||
#include "skl-sst-ipc.h"
|
||||
#include "sound/hdaudio_ext.h"
|
||||
|
||||
|
||||
#define IPC_IXC_STATUS_BITS 24
|
||||
|
@ -130,6 +132,11 @@
|
|||
#define IPC_SRC_QUEUE_MASK 0x7
|
||||
#define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \
|
||||
<< IPC_SRC_QUEUE_SHIFT)
|
||||
/* Load Module count */
|
||||
#define IPC_LOAD_MODULE_SHIFT 0
|
||||
#define IPC_LOAD_MODULE_MASK 0xFF
|
||||
#define IPC_LOAD_MODULE_CNT(x) (((x) & IPC_LOAD_MODULE_MASK) \
|
||||
<< IPC_LOAD_MODULE_SHIFT)
|
||||
|
||||
/* Save pipeline messgae extension register */
|
||||
#define IPC_DMA_ID_SHIFT 0
|
||||
|
@ -317,6 +324,19 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
|
|||
wake_up(&skl->boot_wait);
|
||||
break;
|
||||
|
||||
case IPC_GLB_NOTIFY_PHRASE_DETECTED:
|
||||
dev_dbg(ipc->dev, "***** Phrase Detected **********\n");
|
||||
|
||||
/*
|
||||
* Per HW recomendation, After phrase detection,
|
||||
* clear the CGCTL.MISCBDCGE.
|
||||
*
|
||||
* This will be set back on stream closure
|
||||
*/
|
||||
skl->enable_miscbdcge(ipc->dev, false);
|
||||
skl->miscbdcg_disabled = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(ipc->dev, "ipc: Unhandled error msg=%x",
|
||||
header.primary);
|
||||
|
@ -344,6 +364,8 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
|
|||
switch (reply) {
|
||||
case IPC_GLB_REPLY_SUCCESS:
|
||||
dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
|
||||
/* copy the rx data from the mailbox */
|
||||
sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size);
|
||||
break;
|
||||
|
||||
case IPC_GLB_REPLY_OUT_OF_MEMORY:
|
||||
|
@ -650,7 +672,7 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id,
|
|||
dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
|
||||
header.primary, header.extension);
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
|
||||
dx, sizeof(dx), NULL, 0);
|
||||
dx, sizeof(*dx), NULL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret);
|
||||
return ret;
|
||||
|
@ -728,6 +750,54 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind);
|
||||
|
||||
/*
|
||||
* In order to load a module we need to send IPC to initiate that. DMA will
|
||||
* performed to load the module memory. The FW supports multiple module load
|
||||
* at single shot, so we can send IPC with N modules represented by
|
||||
* module_cnt
|
||||
*/
|
||||
int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
|
||||
u8 module_cnt, void *data)
|
||||
{
|
||||
struct skl_ipc_header header = {0};
|
||||
u64 *ipc_header = (u64 *)(&header);
|
||||
int ret;
|
||||
|
||||
header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
|
||||
header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
|
||||
header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS);
|
||||
header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
|
||||
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
|
||||
(sizeof(u16) * module_cnt), NULL, 0);
|
||||
if (ret < 0)
|
||||
dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_load_modules);
|
||||
|
||||
int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt,
|
||||
void *data)
|
||||
{
|
||||
struct skl_ipc_header header = {0};
|
||||
u64 *ipc_header = (u64 *)(&header);
|
||||
int ret;
|
||||
|
||||
header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
|
||||
header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
|
||||
header.primary |= IPC_GLB_TYPE(IPC_GLB_UNLOAD_MULTIPLE_MODS);
|
||||
header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
|
||||
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
|
||||
(sizeof(u16) * module_cnt), NULL, 0);
|
||||
if (ret < 0)
|
||||
dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_unload_modules);
|
||||
|
||||
int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param)
|
||||
{
|
||||
|
@ -781,3 +851,54 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_set_large_config);
|
||||
|
||||
int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param)
|
||||
{
|
||||
struct skl_ipc_header header = {0};
|
||||
u64 *ipc_header = (u64 *)(&header);
|
||||
int ret = 0;
|
||||
size_t sz_remaining, rx_size, data_offset;
|
||||
|
||||
header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
|
||||
header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
|
||||
header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_GET);
|
||||
header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id);
|
||||
header.primary |= IPC_MOD_ID(msg->module_id);
|
||||
|
||||
header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size);
|
||||
header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id);
|
||||
header.extension |= IPC_FINAL_BLOCK(1);
|
||||
header.extension |= IPC_INITIAL_BLOCK(1);
|
||||
|
||||
sz_remaining = msg->param_data_size;
|
||||
data_offset = 0;
|
||||
|
||||
while (sz_remaining != 0) {
|
||||
rx_size = sz_remaining > SKL_ADSP_W1_SZ
|
||||
? SKL_ADSP_W1_SZ : sz_remaining;
|
||||
if (rx_size == sz_remaining)
|
||||
header.extension |= IPC_FINAL_BLOCK(1);
|
||||
|
||||
ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0,
|
||||
((char *)param) + data_offset,
|
||||
msg->param_data_size);
|
||||
if (ret < 0) {
|
||||
dev_err(ipc->dev,
|
||||
"ipc: get large config fail, err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
sz_remaining -= rx_size;
|
||||
data_offset = msg->param_data_size - sz_remaining;
|
||||
|
||||
/* clear the fields */
|
||||
header.extension &= IPC_INITIAL_BLOCK_CLEAR;
|
||||
header.extension &= IPC_DATA_OFFSET_SZ_CLEAR;
|
||||
/* fill the fields */
|
||||
header.extension |= IPC_INITIAL_BLOCK(1);
|
||||
header.extension |= IPC_DATA_OFFSET_SZ(data_offset);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_ipc_get_large_config);
|
||||
|
|
|
@ -55,6 +55,11 @@ struct skl_sst {
|
|||
|
||||
/* IPC messaging */
|
||||
struct sst_generic_ipc ipc;
|
||||
|
||||
/* callback for miscbdge */
|
||||
void (*enable_miscbdcge)(struct device *dev, bool enable);
|
||||
/*Is CGCTL.MISCBDCGE disabled*/
|
||||
bool miscbdcg_disabled;
|
||||
};
|
||||
|
||||
struct skl_ipc_init_instance_msg {
|
||||
|
@ -108,12 +113,21 @@ int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc,
|
|||
int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc,
|
||||
struct skl_ipc_bind_unbind_msg *msg);
|
||||
|
||||
int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
|
||||
u8 module_cnt, void *data);
|
||||
|
||||
int skl_ipc_unload_modules(struct sst_generic_ipc *ipc,
|
||||
u8 module_cnt, void *data);
|
||||
|
||||
int skl_ipc_set_dx(struct sst_generic_ipc *ipc,
|
||||
u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx);
|
||||
|
||||
int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param);
|
||||
|
||||
int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
|
||||
struct skl_ipc_large_config_msg *msg, u32 *param);
|
||||
|
||||
void skl_ipc_int_enable(struct sst_dsp *dsp);
|
||||
void skl_ipc_op_int_enable(struct sst_dsp *ctx);
|
||||
void skl_ipc_op_int_disable(struct sst_dsp *ctx);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
#include "../common/sst-ipc.h"
|
||||
|
@ -37,6 +38,8 @@
|
|||
#define SKL_INSTANCE_ID 0
|
||||
#define SKL_BASE_FW_MODULE_ID 0
|
||||
|
||||
#define SKL_NUM_MODULES 1
|
||||
|
||||
static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
|
||||
{
|
||||
u32 cur_sts;
|
||||
|
@ -77,7 +80,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
|
|||
init_waitqueue_head(&skl->boot_wait);
|
||||
|
||||
if (ctx->fw == NULL) {
|
||||
ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev);
|
||||
ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
|
||||
skl_dsp_disable_core(ctx);
|
||||
|
@ -115,27 +118,28 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
|
|||
dev_err(ctx->dev,
|
||||
"Timeout waiting for ROM init done, reg:0x%x\n", reg);
|
||||
ret = -EIO;
|
||||
goto skl_load_base_firmware_failed;
|
||||
goto transfer_firmware_failed;
|
||||
}
|
||||
|
||||
ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
|
||||
goto skl_load_base_firmware_failed;
|
||||
goto transfer_firmware_failed;
|
||||
} else {
|
||||
ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
|
||||
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
|
||||
if (ret == 0) {
|
||||
dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n");
|
||||
ret = -EIO;
|
||||
goto skl_load_base_firmware_failed;
|
||||
goto transfer_firmware_failed;
|
||||
}
|
||||
|
||||
dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
|
||||
skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
|
||||
}
|
||||
return 0;
|
||||
|
||||
transfer_firmware_failed:
|
||||
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
|
||||
skl_load_base_firmware_failed:
|
||||
skl_dsp_disable_core(ctx);
|
||||
release_firmware(ctx->fw);
|
||||
|
@ -175,10 +179,15 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
|
|||
dx.core_mask = SKL_DSP_CORE0_MASK;
|
||||
dx.dx_mask = SKL_IPC_D3_MASK;
|
||||
ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Failed to set DSP to D3 state\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev,
|
||||
"D3 request to FW failed, continuing reset: %d", ret);
|
||||
|
||||
/* disable Interrupt */
|
||||
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
|
||||
skl_cldma_int_disable(ctx);
|
||||
skl_ipc_op_int_disable(ctx);
|
||||
skl_ipc_int_disable(ctx);
|
||||
|
||||
ret = skl_dsp_disable_core(ctx);
|
||||
if (ret < 0) {
|
||||
|
@ -187,12 +196,6 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
|
|||
}
|
||||
skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
|
||||
|
||||
/* disable Interrupt */
|
||||
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
|
||||
skl_cldma_int_disable(ctx);
|
||||
skl_ipc_op_int_disable(ctx);
|
||||
skl_ipc_int_disable(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -201,11 +204,182 @@ static unsigned int skl_get_errorcode(struct sst_dsp *ctx)
|
|||
return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE);
|
||||
}
|
||||
|
||||
/*
|
||||
* since get/set_module are called from DAPM context,
|
||||
* we don't need lock for usage count
|
||||
*/
|
||||
static int skl_get_module(struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
struct skl_module_table *module;
|
||||
|
||||
list_for_each_entry(module, &ctx->module_list, list) {
|
||||
if (module->mod_info->mod_id == mod_id)
|
||||
return ++module->usage_cnt;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int skl_put_module(struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
struct skl_module_table *module;
|
||||
|
||||
list_for_each_entry(module, &ctx->module_list, list) {
|
||||
if (module->mod_info->mod_id == mod_id)
|
||||
return --module->usage_cnt;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx,
|
||||
char *mod_name, int mod_id)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
struct skl_module_table *skl_module;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
|
||||
ret = request_firmware(&fw, mod_name, ctx->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Request Module %s failed :%d\n",
|
||||
mod_name, ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL);
|
||||
if (skl_module == NULL) {
|
||||
release_firmware(fw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = sizeof(*skl_module->mod_info);
|
||||
skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
|
||||
if (skl_module->mod_info == NULL) {
|
||||
release_firmware(fw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skl_module->mod_info->mod_id = mod_id;
|
||||
skl_module->mod_info->fw = fw;
|
||||
list_add(&skl_module->list, &ctx->module_list);
|
||||
|
||||
return skl_module;
|
||||
}
|
||||
|
||||
/* get a module from it's unique ID */
|
||||
static struct skl_module_table *skl_module_get_from_id(
|
||||
struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
struct skl_module_table *module;
|
||||
|
||||
if (list_empty(&ctx->module_list)) {
|
||||
dev_err(ctx->dev, "Module list is empty\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(module, &ctx->module_list, list) {
|
||||
if (module->mod_info->mod_id == mod_id)
|
||||
return module;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int skl_transfer_module(struct sst_dsp *ctx,
|
||||
struct skl_load_module_info *module)
|
||||
{
|
||||
int ret;
|
||||
struct skl_sst *skl = ctx->thread_context;
|
||||
|
||||
ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data,
|
||||
module->fw->size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES,
|
||||
(void *)&module->mod_id);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "Failed to Load module: %d\n", ret);
|
||||
|
||||
ctx->cl_dev.ops.cl_stop_dma(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid)
|
||||
{
|
||||
struct skl_module_table *module_entry = NULL;
|
||||
int ret = 0;
|
||||
char mod_name[64]; /* guid str = 32 chars + 4 hyphens */
|
||||
|
||||
snprintf(mod_name, sizeof(mod_name), "%s%s%s",
|
||||
"intel/dsp_fw_", guid, ".bin");
|
||||
|
||||
module_entry = skl_module_get_from_id(ctx, mod_id);
|
||||
if (module_entry == NULL) {
|
||||
module_entry = skl_fill_module_table(ctx, mod_name, mod_id);
|
||||
if (module_entry == NULL) {
|
||||
dev_err(ctx->dev, "Failed to Load module\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!module_entry->usage_cnt) {
|
||||
ret = skl_transfer_module(ctx, module_entry->mod_info);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Failed to Load module\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = skl_get_module(ctx, mod_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
|
||||
{
|
||||
int usage_cnt;
|
||||
struct skl_sst *skl = ctx->thread_context;
|
||||
int ret = 0;
|
||||
|
||||
usage_cnt = skl_put_module(ctx, mod_id);
|
||||
if (usage_cnt < 0) {
|
||||
dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt);
|
||||
return -EIO;
|
||||
}
|
||||
ret = skl_ipc_unload_modules(&skl->ipc,
|
||||
SKL_NUM_MODULES, &mod_id);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "Failed to UnLoad module\n");
|
||||
skl_get_module(ctx, mod_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void skl_clear_module_table(struct sst_dsp *ctx)
|
||||
{
|
||||
struct skl_module_table *module, *tmp;
|
||||
|
||||
if (list_empty(&ctx->module_list))
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(module, tmp, &ctx->module_list, list) {
|
||||
list_del(&module->list);
|
||||
release_firmware(module->mod_info->fw);
|
||||
}
|
||||
}
|
||||
|
||||
static struct skl_dsp_fw_ops skl_fw_ops = {
|
||||
.set_state_D0 = skl_set_dsp_D0,
|
||||
.set_state_D3 = skl_set_dsp_D3,
|
||||
.load_fw = skl_load_base_firmware,
|
||||
.get_fw_errcode = skl_get_errorcode,
|
||||
.load_mod = skl_load_module,
|
||||
.unload_mod = skl_unload_module,
|
||||
};
|
||||
|
||||
static struct sst_ops skl_ops = {
|
||||
|
@ -223,7 +397,7 @@ static struct sst_dsp_device skl_dev = {
|
|||
};
|
||||
|
||||
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
||||
struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp)
|
||||
const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp)
|
||||
{
|
||||
struct skl_sst *skl;
|
||||
struct sst_dsp *sst;
|
||||
|
@ -244,11 +418,13 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
|||
|
||||
sst = skl->dsp;
|
||||
|
||||
sst->fw_name = fw_name;
|
||||
sst->addr.lpe = mmio_base;
|
||||
sst->addr.shim = mmio_base;
|
||||
sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
|
||||
SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
|
||||
|
||||
INIT_LIST_HEAD(&sst->module_list);
|
||||
sst->dsp_ops = dsp_ops;
|
||||
sst->fw_ops = skl_fw_ops;
|
||||
|
||||
|
@ -259,23 +435,24 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
|
|||
ret = sst->fw_ops.load_fw(sst);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Load base fw failed : %d", ret);
|
||||
return ret;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (dsp)
|
||||
*dsp = skl;
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
skl_ipc_free(&skl->ipc);
|
||||
cleanup:
|
||||
skl_sst_dsp_cleanup(dev, skl);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
|
||||
|
||||
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
|
||||
{
|
||||
skl_clear_module_table(ctx->dsp);
|
||||
skl_ipc_free(&ctx->ipc);
|
||||
ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
|
||||
ctx->dsp->ops->free(ctx->dsp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "skl-topology.h"
|
||||
#include "skl.h"
|
||||
#include "skl-tplg-interface.h"
|
||||
#include "../common/sst-dsp.h"
|
||||
#include "../common/sst-dsp-priv.h"
|
||||
|
||||
#define SKL_CH_FIXUP_MASK (1 << 0)
|
||||
#define SKL_RATE_FIXUP_MASK (1 << 1)
|
||||
|
@ -129,17 +131,15 @@ static void skl_dump_mconfig(struct skl_sst *ctx,
|
|||
{
|
||||
dev_dbg(ctx->dev, "Dumping config\n");
|
||||
dev_dbg(ctx->dev, "Input Format:\n");
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n",
|
||||
mcfg->in_fmt.valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt[0].channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt[0].s_freq);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt[0].ch_cfg);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->in_fmt[0].valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "Output Format:\n");
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n",
|
||||
mcfg->out_fmt.valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
|
||||
dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt[0].channels);
|
||||
dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt[0].s_freq);
|
||||
dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->out_fmt[0].valid_bit_depth);
|
||||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
|
||||
}
|
||||
|
||||
static void skl_tplg_update_params(struct skl_module_fmt *fmt,
|
||||
|
@ -149,8 +149,24 @@ static void skl_tplg_update_params(struct skl_module_fmt *fmt,
|
|||
fmt->s_freq = params->s_freq;
|
||||
if (fixup & SKL_CH_FIXUP_MASK)
|
||||
fmt->channels = params->ch;
|
||||
if (fixup & SKL_FMT_FIXUP_MASK)
|
||||
fmt->valid_bit_depth = params->s_fmt;
|
||||
if (fixup & SKL_FMT_FIXUP_MASK) {
|
||||
fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
|
||||
|
||||
/*
|
||||
* 16 bit is 16 bit container whereas 24 bit is in 32 bit
|
||||
* container so update bit depth accordingly
|
||||
*/
|
||||
switch (fmt->valid_bit_depth) {
|
||||
case SKL_DEPTH_16BIT:
|
||||
fmt->bit_depth = fmt->valid_bit_depth;
|
||||
break;
|
||||
|
||||
default:
|
||||
fmt->bit_depth = SKL_DEPTH_32BIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -171,8 +187,9 @@ static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
|
|||
int in_fixup, out_fixup;
|
||||
struct skl_module_fmt *in_fmt, *out_fmt;
|
||||
|
||||
in_fmt = &m_cfg->in_fmt;
|
||||
out_fmt = &m_cfg->out_fmt;
|
||||
/* Fixups will be applied to pin 0 only */
|
||||
in_fmt = &m_cfg->in_fmt[0];
|
||||
out_fmt = &m_cfg->out_fmt[0];
|
||||
|
||||
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (is_fe) {
|
||||
|
@ -209,18 +226,25 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
|
|||
struct skl_module_cfg *mcfg)
|
||||
{
|
||||
int multiplier = 1;
|
||||
struct skl_module_fmt *in_fmt, *out_fmt;
|
||||
|
||||
|
||||
/* Since fixups is applied to pin 0 only, ibs, obs needs
|
||||
* change for pin 0 only
|
||||
*/
|
||||
in_fmt = &mcfg->in_fmt[0];
|
||||
out_fmt = &mcfg->out_fmt[0];
|
||||
|
||||
if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
|
||||
multiplier = 5;
|
||||
|
||||
mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
|
||||
(mcfg->in_fmt.channels) *
|
||||
(mcfg->in_fmt.bit_depth >> 3) *
|
||||
mcfg->ibs = (in_fmt->s_freq / 1000) *
|
||||
(mcfg->in_fmt->channels) *
|
||||
(mcfg->in_fmt->bit_depth >> 3) *
|
||||
multiplier;
|
||||
|
||||
mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
|
||||
(mcfg->out_fmt.channels) *
|
||||
(mcfg->out_fmt.bit_depth >> 3) *
|
||||
mcfg->obs = (mcfg->out_fmt->s_freq / 1000) *
|
||||
(mcfg->out_fmt->channels) *
|
||||
(mcfg->out_fmt->bit_depth >> 3) *
|
||||
multiplier;
|
||||
}
|
||||
|
||||
|
@ -291,6 +315,83 @@ static int skl_tplg_alloc_pipe_widget(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* some modules can have multiple params set from user control and
|
||||
* need to be set after module is initialized. If set_param flag is
|
||||
* set module params will be done after module is initialised.
|
||||
*/
|
||||
static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w,
|
||||
struct skl_sst *ctx)
|
||||
{
|
||||
int i, ret;
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
const struct snd_kcontrol_new *k;
|
||||
struct soc_bytes_ext *sb;
|
||||
struct skl_algo_data *bc;
|
||||
struct skl_specific_cfg *sp_cfg;
|
||||
|
||||
if (mconfig->formats_config.caps_size > 0 &&
|
||||
mconfig->formats_config.set_params == SKL_PARAM_SET) {
|
||||
sp_cfg = &mconfig->formats_config;
|
||||
ret = skl_set_module_params(ctx, sp_cfg->caps,
|
||||
sp_cfg->caps_size,
|
||||
sp_cfg->param_id, mconfig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < w->num_kcontrols; i++) {
|
||||
k = &w->kcontrol_news[i];
|
||||
if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
||||
sb = (void *) k->private_value;
|
||||
bc = (struct skl_algo_data *)sb->dobj.private;
|
||||
|
||||
if (bc->set_params == SKL_PARAM_SET) {
|
||||
ret = skl_set_module_params(ctx,
|
||||
(u32 *)bc->params, bc->max,
|
||||
bc->param_id, mconfig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* some module param can set from user control and this is required as
|
||||
* when module is initailzed. if module param is required in init it is
|
||||
* identifed by set_param flag. if set_param flag is not set, then this
|
||||
* parameter needs to set as part of module init.
|
||||
*/
|
||||
static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
|
||||
{
|
||||
const struct snd_kcontrol_new *k;
|
||||
struct soc_bytes_ext *sb;
|
||||
struct skl_algo_data *bc;
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < w->num_kcontrols; i++) {
|
||||
k = &w->kcontrol_news[i];
|
||||
if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
||||
sb = (struct soc_bytes_ext *)k->private_value;
|
||||
bc = (struct skl_algo_data *)sb->dobj.private;
|
||||
|
||||
if (bc->set_params != SKL_PARAM_INIT)
|
||||
continue;
|
||||
|
||||
mconfig->formats_config.caps = (u32 *)&bc->params;
|
||||
mconfig->formats_config.caps_size = bc->max;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inside a pipe instance, we can have various modules. These modules need
|
||||
* to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
|
||||
|
@ -313,12 +414,25 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
|
|||
if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
|
||||
return -ENOMEM;
|
||||
|
||||
if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
|
||||
ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
|
||||
mconfig->id.module_id, mconfig->guid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* apply fix/conversion to module params based on
|
||||
* FE/BE params
|
||||
*/
|
||||
skl_tplg_update_module_params(w, ctx);
|
||||
ret = skl_init_module(ctx, mconfig, NULL);
|
||||
|
||||
skl_tplg_set_module_init_data(w);
|
||||
ret = skl_init_module(ctx, mconfig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_tplg_set_module_params(w, ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -326,6 +440,24 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
|
||||
struct skl_pipe *pipe)
|
||||
{
|
||||
struct skl_pipe_module *w_module = NULL;
|
||||
struct skl_module_cfg *mconfig = NULL;
|
||||
|
||||
list_for_each_entry(w_module, &pipe->w_list, node) {
|
||||
mconfig = w_module->w->priv;
|
||||
|
||||
if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod)
|
||||
return ctx->dsp->fw_ops.unload_mod(ctx->dsp,
|
||||
mconfig->id.module_id);
|
||||
}
|
||||
|
||||
/* no modules to unload in this path, so return */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
|
||||
* need create the pipeline. So we do following:
|
||||
|
@ -397,6 +529,58 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl,
|
||||
struct skl_module_cfg *src_mconfig)
|
||||
{
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct snd_soc_dapm_widget *sink = NULL, *next_sink = NULL;
|
||||
struct skl_module_cfg *sink_mconfig;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
int ret;
|
||||
|
||||
snd_soc_dapm_widget_for_each_sink_path(w, p) {
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
|
||||
dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
|
||||
|
||||
next_sink = p->sink;
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that
|
||||
* can be any widgets type and we are only interested if
|
||||
* they are ones used for SKL so check that first
|
||||
*/
|
||||
if ((p->sink->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->sink)) {
|
||||
|
||||
sink = p->sink;
|
||||
sink_mconfig = sink->priv;
|
||||
|
||||
/* Bind source to sink, mixin is always source */
|
||||
ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start sinks pipe first */
|
||||
if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
|
||||
if (sink_mconfig->pipe->conn_type !=
|
||||
SKL_PIPE_CONN_TYPE_FE)
|
||||
ret = skl_run_pipe(ctx,
|
||||
sink_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sink)
|
||||
return skl_tplg_bind_sinks(next_sink, skl, src_mconfig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
|
||||
* we need to do following:
|
||||
|
@ -408,75 +592,62 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
|
|||
* - Then run current pipe
|
||||
*/
|
||||
static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl)
|
||||
struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct skl_dapm_path_list *path_list;
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
struct skl_module_cfg *src_mconfig;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
int ret = 0;
|
||||
|
||||
source = w;
|
||||
src_mconfig = source->priv;
|
||||
src_mconfig = w->priv;
|
||||
|
||||
/*
|
||||
* find which sink it is connected to, bind with the sink,
|
||||
* if sink is not started, start sink pipe first, then start
|
||||
* this pipe
|
||||
*/
|
||||
snd_soc_dapm_widget_for_each_source_path(w, p) {
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
|
||||
dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
|
||||
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that
|
||||
* can be any widgets type and we are only interested if
|
||||
* they are ones used for SKL so check that first
|
||||
*/
|
||||
if ((p->sink->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->sink)) {
|
||||
|
||||
sink = p->sink;
|
||||
src_mconfig = source->priv;
|
||||
sink_mconfig = sink->priv;
|
||||
|
||||
/* Bind source to sink, mixin is always source */
|
||||
ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start sinks pipe first */
|
||||
if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
|
||||
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
path_list = kzalloc(
|
||||
sizeof(struct skl_dapm_path_list),
|
||||
GFP_KERNEL);
|
||||
if (path_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Add connected path to one global list */
|
||||
path_list->dapm_path = p;
|
||||
list_add_tail(&path_list->node, &skl->dapm_path_list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start source pipe last after starting all sinks */
|
||||
ret = skl_run_pipe(ctx, src_mconfig->pipe);
|
||||
ret = skl_tplg_bind_sinks(w, skl, src_mconfig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start source pipe last after starting all sinks */
|
||||
if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
|
||||
return skl_run_pipe(ctx, src_mconfig->pipe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dapm_widget *skl_get_src_dsp_widget(
|
||||
struct snd_soc_dapm_widget *w, struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct snd_soc_dapm_widget *src_w = NULL;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
|
||||
snd_soc_dapm_widget_for_each_source_path(w, p) {
|
||||
src_w = p->source;
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
|
||||
dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
|
||||
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that can
|
||||
* be any widgets type and we are only interested if they are
|
||||
* ones used for SKL so check that first
|
||||
*/
|
||||
if ((p->source->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->source)) {
|
||||
return p->source;
|
||||
}
|
||||
}
|
||||
|
||||
if (src_w != NULL)
|
||||
return skl_get_src_dsp_widget(src_w, skl);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* in the Post-PMU event of mixer we need to do following:
|
||||
* - Check if this pipe is running
|
||||
|
@ -490,7 +661,6 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
|||
struct skl *skl)
|
||||
{
|
||||
int ret = 0;
|
||||
struct snd_soc_dapm_path *p;
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
|
@ -504,32 +674,18 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
|||
* one more sink before this sink got connected, Since source is
|
||||
* started, bind this sink to source and start this pipe.
|
||||
*/
|
||||
snd_soc_dapm_widget_for_each_sink_path(w, p) {
|
||||
if (!p->connect)
|
||||
continue;
|
||||
|
||||
dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
|
||||
dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
|
||||
source = skl_get_src_dsp_widget(w, skl);
|
||||
if (source != NULL) {
|
||||
src_mconfig = source->priv;
|
||||
sink_mconfig = sink->priv;
|
||||
src_pipe_started = 1;
|
||||
|
||||
/*
|
||||
* here we will check widgets in sink pipelines, so that
|
||||
* can be any widgets type and we are only interested if
|
||||
* they are ones used for SKL so check that first
|
||||
* check pipe state, then no need to bind or start the
|
||||
* pipe
|
||||
*/
|
||||
if ((p->source->priv != NULL) &&
|
||||
is_skl_dsp_widget_type(p->source)) {
|
||||
source = p->source;
|
||||
src_mconfig = source->priv;
|
||||
sink_mconfig = sink->priv;
|
||||
src_pipe_started = 1;
|
||||
|
||||
/*
|
||||
* check pipe state, then no need to bind or start
|
||||
* the pipe
|
||||
*/
|
||||
if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
|
||||
src_pipe_started = 0;
|
||||
}
|
||||
if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
|
||||
src_pipe_started = 0;
|
||||
}
|
||||
|
||||
if (src_pipe_started) {
|
||||
|
@ -537,7 +693,8 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
|
||||
if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
|
||||
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -552,54 +709,37 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
|
|||
static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
int ret = 0, path_found = 0;
|
||||
struct skl_dapm_path_list *path_list, *tmp_list;
|
||||
int ret = 0, i;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
|
||||
sink = w;
|
||||
sink_mconfig = sink->priv;
|
||||
sink_mconfig = w->priv;
|
||||
|
||||
/* Stop the pipe */
|
||||
ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* This list, dapm_path_list handling here does not need any locks
|
||||
* as we are under dapm lock while handling widget events.
|
||||
* List can be manipulated safely only under dapm widgets handler
|
||||
* routines
|
||||
*/
|
||||
list_for_each_entry_safe(path_list, tmp_list,
|
||||
&skl->dapm_path_list, node) {
|
||||
if (path_list->dapm_path->sink == sink) {
|
||||
dev_dbg(ctx->dev, "Path found = %s\n",
|
||||
path_list->dapm_path->name);
|
||||
source = path_list->dapm_path->source;
|
||||
src_mconfig = source->priv;
|
||||
path_found = 1;
|
||||
for (i = 0; i < sink_mconfig->max_in_queue; i++) {
|
||||
if (sink_mconfig->m_in_pin[i].pin_state == SKL_PIN_BIND_DONE) {
|
||||
src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg;
|
||||
if (!src_mconfig)
|
||||
continue;
|
||||
/*
|
||||
* If path_found == 1, that means pmd for source
|
||||
* pipe has not occurred, source is connected to
|
||||
* some other sink. so its responsibility of sink
|
||||
* to unbind itself from source.
|
||||
*/
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
list_del(&path_list->node);
|
||||
kfree(path_list);
|
||||
break;
|
||||
ret = skl_unbind_modules(ctx,
|
||||
src_mconfig, sink_mconfig);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If path_found == 1, that means pmd for source pipe has
|
||||
* not occurred, source is connected to some other sink.
|
||||
* so its responsibility of sink to unbind itself from source.
|
||||
*/
|
||||
if (path_found) {
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -622,10 +762,12 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
|||
int ret = 0;
|
||||
|
||||
skl_tplg_free_pipe_mcps(skl, mconfig);
|
||||
skl_tplg_free_pipe_mem(skl, mconfig);
|
||||
|
||||
list_for_each_entry(w_module, &s_pipe->w_list, node) {
|
||||
dst_module = w_module->w->priv;
|
||||
|
||||
skl_tplg_free_pipe_mcps(skl, dst_module);
|
||||
if (src_module == NULL) {
|
||||
src_module = dst_module;
|
||||
continue;
|
||||
|
@ -639,9 +781,8 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
|||
}
|
||||
|
||||
ret = skl_delete_pipe(ctx, mconfig->pipe);
|
||||
skl_tplg_free_pipe_mem(skl, mconfig);
|
||||
|
||||
return ret;
|
||||
return skl_tplg_unload_pipe_modules(ctx, s_pipe);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -653,47 +794,34 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
|||
static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
||||
struct skl *skl)
|
||||
{
|
||||
struct snd_soc_dapm_widget *source, *sink;
|
||||
struct skl_module_cfg *src_mconfig, *sink_mconfig;
|
||||
int ret = 0, path_found = 0;
|
||||
struct skl_dapm_path_list *path_list, *tmp_path_list;
|
||||
int ret = 0, i;
|
||||
struct skl_sst *ctx = skl->skl_sst;
|
||||
|
||||
source = w;
|
||||
src_mconfig = source->priv;
|
||||
src_mconfig = w->priv;
|
||||
|
||||
skl_tplg_free_pipe_mcps(skl, src_mconfig);
|
||||
/* Stop the pipe since this is a mixin module */
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
|
||||
if (path_list->dapm_path->source == source) {
|
||||
dev_dbg(ctx->dev, "Path found = %s\n",
|
||||
path_list->dapm_path->name);
|
||||
sink = path_list->dapm_path->sink;
|
||||
sink_mconfig = sink->priv;
|
||||
path_found = 1;
|
||||
|
||||
list_del(&path_list->node);
|
||||
kfree(path_list);
|
||||
break;
|
||||
for (i = 0; i < src_mconfig->max_out_queue; i++) {
|
||||
if (src_mconfig->m_out_pin[i].pin_state == SKL_PIN_BIND_DONE) {
|
||||
sink_mconfig = src_mconfig->m_out_pin[i].tgt_mcfg;
|
||||
if (!sink_mconfig)
|
||||
continue;
|
||||
/*
|
||||
* This is a connecter and if path is found that means
|
||||
* unbind between source and sink has not happened yet
|
||||
*/
|
||||
ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = skl_unbind_modules(ctx, src_mconfig,
|
||||
sink_mconfig);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a connector and if path is found that means
|
||||
* unbind between source and sink has not happened yet
|
||||
*/
|
||||
if (path_found) {
|
||||
ret = skl_stop_pipe(ctx, src_mconfig->pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -774,6 +902,67 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
|
||||
unsigned int __user *data, unsigned int size)
|
||||
{
|
||||
struct soc_bytes_ext *sb =
|
||||
(struct soc_bytes_ext *)kcontrol->private_value;
|
||||
struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private;
|
||||
struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
struct skl *skl = get_skl_ctx(w->dapm->dev);
|
||||
|
||||
if (w->power)
|
||||
skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
|
||||
bc->max, bc->param_id, mconfig);
|
||||
|
||||
if (bc->params) {
|
||||
if (copy_to_user(data, &bc->param_id, sizeof(u32)))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(data + 1, &size, sizeof(u32)))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(data + 2, bc->params, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SKL_PARAM_VENDOR_ID 0xff
|
||||
|
||||
static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
|
||||
const unsigned int __user *data, unsigned int size)
|
||||
{
|
||||
struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
|
||||
struct skl_module_cfg *mconfig = w->priv;
|
||||
struct soc_bytes_ext *sb =
|
||||
(struct soc_bytes_ext *)kcontrol->private_value;
|
||||
struct skl_algo_data *ac = (struct skl_algo_data *)sb->dobj.private;
|
||||
struct skl *skl = get_skl_ctx(w->dapm->dev);
|
||||
|
||||
if (ac->params) {
|
||||
/*
|
||||
* if the param_is is of type Vendor, firmware expects actual
|
||||
* parameter id and size from the control.
|
||||
*/
|
||||
if (ac->param_id == SKL_PARAM_VENDOR_ID) {
|
||||
if (copy_from_user(ac->params, data, size))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_from_user(ac->params,
|
||||
data + 2 * sizeof(u32), size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (w->power)
|
||||
return skl_set_module_params(skl->skl_sst,
|
||||
(u32 *)ac->params, ac->max,
|
||||
ac->param_id, mconfig);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The FE params are passed by hw_params of the DAI.
|
||||
* On hw_params, the params are stored in Gateway module of the FE and we
|
||||
|
@ -790,9 +979,9 @@ int skl_tplg_update_pipe_params(struct device *dev,
|
|||
memcpy(pipe->p_params, params, sizeof(*params));
|
||||
|
||||
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
format = &mconfig->in_fmt;
|
||||
format = &mconfig->in_fmt[0];
|
||||
else
|
||||
format = &mconfig->out_fmt;
|
||||
format = &mconfig->out_fmt[0];
|
||||
|
||||
/* set the hw_params */
|
||||
format->s_freq = params->s_freq;
|
||||
|
@ -809,6 +998,7 @@ int skl_tplg_update_pipe_params(struct device *dev,
|
|||
break;
|
||||
|
||||
case SKL_DEPTH_24BIT:
|
||||
case SKL_DEPTH_32BIT:
|
||||
format->bit_depth = SKL_DEPTH_32BIT;
|
||||
break;
|
||||
|
||||
|
@ -846,7 +1036,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
|
|||
w = dai->playback_widget;
|
||||
snd_soc_dapm_widget_for_each_sink_path(w, p) {
|
||||
if (p->connect && p->sink->power &&
|
||||
is_skl_dsp_widget_type(p->sink))
|
||||
!is_skl_dsp_widget_type(p->sink))
|
||||
continue;
|
||||
|
||||
if (p->sink->priv) {
|
||||
|
@ -859,7 +1049,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
|
|||
w = dai->capture_widget;
|
||||
snd_soc_dapm_widget_for_each_source_path(w, p) {
|
||||
if (p->connect && p->source->power &&
|
||||
is_skl_dsp_widget_type(p->source))
|
||||
!is_skl_dsp_widget_type(p->source))
|
||||
continue;
|
||||
|
||||
if (p->source->priv) {
|
||||
|
@ -920,6 +1110,9 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
|
|||
|
||||
memcpy(pipe->p_params, params, sizeof(*params));
|
||||
|
||||
if (link_type == NHLT_LINK_HDA)
|
||||
return 0;
|
||||
|
||||
/* update the blob based on virtual bus_id*/
|
||||
cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
|
||||
params->s_fmt, params->ch,
|
||||
|
@ -950,18 +1143,13 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
|
|||
if (p->connect && is_skl_dsp_widget_type(p->source) &&
|
||||
p->source->priv) {
|
||||
|
||||
if (!p->source->power) {
|
||||
ret = skl_tplg_be_fill_pipe_params(
|
||||
dai, p->source->priv,
|
||||
params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = skl_tplg_be_fill_pipe_params(dai,
|
||||
p->source->priv, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = skl_tplg_be_set_src_pipe_params(
|
||||
dai, p->source, params);
|
||||
ret = skl_tplg_be_set_src_pipe_params(dai,
|
||||
p->source, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -980,15 +1168,10 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
|
|||
if (p->connect && is_skl_dsp_widget_type(p->sink) &&
|
||||
p->sink->priv) {
|
||||
|
||||
if (!p->sink->power) {
|
||||
ret = skl_tplg_be_fill_pipe_params(
|
||||
dai, p->sink->priv, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = skl_tplg_be_fill_pipe_params(dai,
|
||||
p->sink->priv, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = skl_tplg_be_set_sink_pipe_params(
|
||||
dai, p->sink, params);
|
||||
|
@ -1030,6 +1213,11 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
|
|||
{SKL_PGA_EVENT, skl_tplg_pga_event},
|
||||
};
|
||||
|
||||
static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = {
|
||||
{SKL_CONTROL_TYPE_BYTE_TLV, skl_tplg_tlv_control_get,
|
||||
skl_tplg_tlv_control_set},
|
||||
};
|
||||
|
||||
/*
|
||||
* The topology binary passes the pin info for a module so initialize the pin
|
||||
* info passed into module instance
|
||||
|
@ -1045,6 +1233,7 @@ static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
|
|||
m_pin[i].id.instance_id = dfw_pin[i].instance_id;
|
||||
m_pin[i].in_use = false;
|
||||
m_pin[i].is_dynamic = is_dynamic;
|
||||
m_pin[i].pin_state = SKL_PIN_UNBIND;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1092,6 +1281,24 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
|
|||
return ppl->pipe;
|
||||
}
|
||||
|
||||
static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
|
||||
struct skl_dfw_module_fmt *src_fmt,
|
||||
int pins)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pins; i++) {
|
||||
dst_fmt[i].channels = src_fmt[i].channels;
|
||||
dst_fmt[i].s_freq = src_fmt[i].freq;
|
||||
dst_fmt[i].bit_depth = src_fmt[i].bit_depth;
|
||||
dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth;
|
||||
dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg;
|
||||
dst_fmt[i].ch_map = src_fmt[i].ch_map;
|
||||
dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style;
|
||||
dst_fmt[i].sample_type = src_fmt[i].sample_type;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Topology core widget load callback
|
||||
*
|
||||
|
@ -1130,22 +1337,16 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
|||
mconfig->max_in_queue = dfw_config->max_in_queue;
|
||||
mconfig->max_out_queue = dfw_config->max_out_queue;
|
||||
mconfig->is_loadable = dfw_config->is_loadable;
|
||||
mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
|
||||
mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
|
||||
mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
|
||||
mconfig->in_fmt.valid_bit_depth =
|
||||
dfw_config->in_fmt.valid_bit_depth;
|
||||
mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
|
||||
mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
|
||||
mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
|
||||
mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
|
||||
mconfig->out_fmt.valid_bit_depth =
|
||||
dfw_config->out_fmt.valid_bit_depth;
|
||||
mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
|
||||
skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt,
|
||||
MODULE_MAX_IN_PINS);
|
||||
skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt,
|
||||
MODULE_MAX_OUT_PINS);
|
||||
|
||||
mconfig->params_fixup = dfw_config->params_fixup;
|
||||
mconfig->converter = dfw_config->converter;
|
||||
mconfig->m_type = dfw_config->module_type;
|
||||
mconfig->vbus_id = dfw_config->vbus_id;
|
||||
mconfig->mem_pages = dfw_config->mem_pages;
|
||||
|
||||
pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
|
||||
if (pipe)
|
||||
|
@ -1156,10 +1357,13 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
|||
mconfig->time_slot = dfw_config->time_slot;
|
||||
mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
|
||||
|
||||
mconfig->m_in_pin = devm_kzalloc(bus->dev,
|
||||
(mconfig->max_in_queue) *
|
||||
sizeof(*mconfig->m_in_pin),
|
||||
GFP_KERNEL);
|
||||
if (dfw_config->is_loadable)
|
||||
memcpy(mconfig->guid, dfw_config->uuid,
|
||||
ARRAY_SIZE(dfw_config->uuid));
|
||||
|
||||
mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
|
||||
sizeof(*mconfig->m_in_pin),
|
||||
GFP_KERNEL);
|
||||
if (!mconfig->m_in_pin)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1188,7 +1392,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
|||
return -ENOMEM;
|
||||
|
||||
memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
|
||||
dfw_config->caps.caps_size);
|
||||
dfw_config->caps.caps_size);
|
||||
mconfig->formats_config.param_id = dfw_config->caps.param_id;
|
||||
mconfig->formats_config.set_params = dfw_config->caps.set_params;
|
||||
|
||||
bind_event:
|
||||
if (tplg_w->event_type == 0) {
|
||||
|
@ -1209,8 +1415,70 @@ bind_event:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
|
||||
struct snd_soc_tplg_bytes_control *bc)
|
||||
{
|
||||
struct skl_algo_data *ac;
|
||||
struct skl_dfw_algo_data *dfw_ac =
|
||||
(struct skl_dfw_algo_data *)bc->priv.data;
|
||||
|
||||
ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL);
|
||||
if (!ac)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Fill private data */
|
||||
ac->max = dfw_ac->max;
|
||||
ac->param_id = dfw_ac->param_id;
|
||||
ac->set_params = dfw_ac->set_params;
|
||||
|
||||
if (ac->max) {
|
||||
ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
|
||||
if (!ac->params)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dfw_ac->params)
|
||||
memcpy(ac->params, dfw_ac->params, ac->max);
|
||||
}
|
||||
|
||||
be->dobj.private = ac;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
|
||||
struct snd_kcontrol_new *kctl,
|
||||
struct snd_soc_tplg_ctl_hdr *hdr)
|
||||
{
|
||||
struct soc_bytes_ext *sb;
|
||||
struct snd_soc_tplg_bytes_control *tplg_bc;
|
||||
struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
switch (hdr->ops.info) {
|
||||
case SND_SOC_TPLG_CTL_BYTES:
|
||||
tplg_bc = container_of(hdr,
|
||||
struct snd_soc_tplg_bytes_control, hdr);
|
||||
if (kctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
|
||||
sb = (struct soc_bytes_ext *)kctl->private_value;
|
||||
if (tplg_bc->priv.size)
|
||||
return skl_init_algo_data(
|
||||
bus->dev, sb, tplg_bc);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(bus->dev, "Control load not supported %d:%d:%d\n",
|
||||
hdr->ops.get, hdr->ops.put, hdr->ops.info);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_tplg_ops skl_tplg_ops = {
|
||||
.widget_load = skl_tplg_widget_load,
|
||||
.control_load = skl_tplg_control_load,
|
||||
.bytes_ext_ops = skl_tlv_ops,
|
||||
.bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
|
||||
};
|
||||
|
||||
/* This will be read from topology manifest, currently defined here */
|
||||
|
|
|
@ -36,6 +36,9 @@
|
|||
/* Maximum number of coefficients up down mixer module */
|
||||
#define UP_DOWN_MIXER_MAX_COEFF 6
|
||||
|
||||
#define MODULE_MAX_IN_PINS 8
|
||||
#define MODULE_MAX_OUT_PINS 8
|
||||
|
||||
enum skl_channel_index {
|
||||
SKL_CHANNEL_LEFT = 0,
|
||||
SKL_CHANNEL_RIGHT = 1,
|
||||
|
@ -55,12 +58,6 @@ enum skl_bitdepth {
|
|||
SKL_DEPTH_INVALID
|
||||
};
|
||||
|
||||
enum skl_interleaving {
|
||||
/* [s1_ch1...s1_chN,...,sM_ch1...sM_chN] */
|
||||
SKL_INTERLEAVING_PER_CHANNEL = 0,
|
||||
/* [s1_ch1...sM_ch1,...,s1_chN...sM_chN] */
|
||||
SKL_INTERLEAVING_PER_SAMPLE = 1,
|
||||
};
|
||||
|
||||
enum skl_s_freq {
|
||||
SKL_FS_8000 = 8000,
|
||||
|
@ -143,6 +140,16 @@ struct skl_up_down_mixer_cfg {
|
|||
s32 coeff[UP_DOWN_MIXER_MAX_COEFF];
|
||||
} __packed;
|
||||
|
||||
struct skl_algo_cfg {
|
||||
struct skl_base_cfg base_cfg;
|
||||
char params[0];
|
||||
} __packed;
|
||||
|
||||
struct skl_base_outfmt_cfg {
|
||||
struct skl_base_cfg base_cfg;
|
||||
struct skl_audio_data_format out_fmt;
|
||||
} __packed;
|
||||
|
||||
enum skl_dma_type {
|
||||
SKL_DMA_HDA_HOST_OUTPUT_CLASS = 0,
|
||||
SKL_DMA_HDA_HOST_INPUT_CLASS = 1,
|
||||
|
@ -178,21 +185,34 @@ struct skl_module_fmt {
|
|||
u32 bit_depth;
|
||||
u32 valid_bit_depth;
|
||||
u32 ch_cfg;
|
||||
u32 interleaving_style;
|
||||
u32 sample_type;
|
||||
u32 ch_map;
|
||||
};
|
||||
|
||||
struct skl_module_cfg;
|
||||
|
||||
struct skl_module_inst_id {
|
||||
u32 module_id;
|
||||
u32 instance_id;
|
||||
};
|
||||
|
||||
enum skl_module_pin_state {
|
||||
SKL_PIN_UNBIND = 0,
|
||||
SKL_PIN_BIND_DONE = 1,
|
||||
};
|
||||
|
||||
struct skl_module_pin {
|
||||
struct skl_module_inst_id id;
|
||||
u8 pin_index;
|
||||
bool is_dynamic;
|
||||
bool in_use;
|
||||
enum skl_module_pin_state pin_state;
|
||||
struct skl_module_cfg *tgt_mcfg;
|
||||
};
|
||||
|
||||
struct skl_specific_cfg {
|
||||
u32 set_params;
|
||||
u32 param_id;
|
||||
u32 caps_size;
|
||||
u32 *caps;
|
||||
};
|
||||
|
@ -238,9 +258,13 @@ enum skl_module_state {
|
|||
};
|
||||
|
||||
struct skl_module_cfg {
|
||||
char guid[SKL_UUID_STR_SZ];
|
||||
struct skl_module_inst_id id;
|
||||
struct skl_module_fmt in_fmt;
|
||||
struct skl_module_fmt out_fmt;
|
||||
u8 domain;
|
||||
bool homogenous_inputs;
|
||||
bool homogenous_outputs;
|
||||
struct skl_module_fmt in_fmt[MODULE_MAX_IN_PINS];
|
||||
struct skl_module_fmt out_fmt[MODULE_MAX_OUT_PINS];
|
||||
u8 max_in_queue;
|
||||
u8 max_out_queue;
|
||||
u8 in_queue_mask;
|
||||
|
@ -258,6 +282,7 @@ struct skl_module_cfg {
|
|||
u32 params_fixup;
|
||||
u32 converter;
|
||||
u32 vbus_id;
|
||||
u32 mem_pages;
|
||||
struct skl_module_pin *m_in_pin;
|
||||
struct skl_module_pin *m_out_pin;
|
||||
enum skl_module_type m_type;
|
||||
|
@ -267,13 +292,15 @@ struct skl_module_cfg {
|
|||
struct skl_specific_cfg formats_config;
|
||||
};
|
||||
|
||||
struct skl_pipeline {
|
||||
struct skl_pipe *pipe;
|
||||
struct list_head node;
|
||||
struct skl_algo_data {
|
||||
u32 param_id;
|
||||
u32 set_params;
|
||||
u32 max;
|
||||
char *params;
|
||||
};
|
||||
|
||||
struct skl_dapm_path_list {
|
||||
struct snd_soc_dapm_path *dapm_path;
|
||||
struct skl_pipeline {
|
||||
struct skl_pipe *pipe;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
|
@ -305,8 +332,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
|
|||
|
||||
int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
|
||||
|
||||
int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config,
|
||||
char *param);
|
||||
int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config);
|
||||
|
||||
int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
|
||||
*src_module, struct skl_module_cfg *dst_module);
|
||||
|
@ -314,5 +340,10 @@ int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
|
|||
int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg
|
||||
*src_module, struct skl_module_cfg *dst_module);
|
||||
|
||||
int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg);
|
||||
int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
|
||||
u32 param_id, struct skl_module_cfg *mcfg);
|
||||
|
||||
enum skl_bitdepth skl_get_bit_depth(int params);
|
||||
#endif
|
||||
|
|
|
@ -23,15 +23,13 @@
|
|||
* Default types range from 0~12. type can range from 0 to 0xff
|
||||
* SST types start at higher to avoid any overlapping in future
|
||||
*/
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101
|
||||
#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103
|
||||
#define SKL_CONTROL_TYPE_BYTE_TLV 0x100
|
||||
|
||||
#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/
|
||||
#define MAX_IN_QUEUE 8
|
||||
#define MAX_OUT_QUEUE 8
|
||||
|
||||
#define SKL_UUID_STR_SZ 40
|
||||
/* Event types goes here */
|
||||
/* Reserve event type 0 for no event handlers */
|
||||
enum skl_event_types {
|
||||
|
@ -72,6 +70,7 @@ enum skl_ch_cfg {
|
|||
SKL_CH_CFG_DUAL_MONO = 9,
|
||||
SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10,
|
||||
SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11,
|
||||
SKL_CH_CFG_4_CHANNEL = 12,
|
||||
SKL_CH_CFG_INVALID
|
||||
};
|
||||
|
||||
|
@ -79,7 +78,9 @@ enum skl_module_type {
|
|||
SKL_MODULE_TYPE_MIXER = 0,
|
||||
SKL_MODULE_TYPE_COPIER,
|
||||
SKL_MODULE_TYPE_UPDWMIX,
|
||||
SKL_MODULE_TYPE_SRCINT
|
||||
SKL_MODULE_TYPE_SRCINT,
|
||||
SKL_MODULE_TYPE_ALGO,
|
||||
SKL_MODULE_TYPE_BASE_OUTFMT
|
||||
};
|
||||
|
||||
enum skl_core_affinity {
|
||||
|
@ -110,6 +111,42 @@ enum skl_dev_type {
|
|||
SKL_DEVICE_NONE
|
||||
};
|
||||
|
||||
/**
|
||||
* enum skl_interleaving - interleaving style
|
||||
*
|
||||
* @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN]
|
||||
* @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN]
|
||||
*/
|
||||
enum skl_interleaving {
|
||||
SKL_INTERLEAVING_PER_CHANNEL = 0,
|
||||
SKL_INTERLEAVING_PER_SAMPLE = 1,
|
||||
};
|
||||
|
||||
enum skl_sample_type {
|
||||
SKL_SAMPLE_TYPE_INT_MSB = 0,
|
||||
SKL_SAMPLE_TYPE_INT_LSB = 1,
|
||||
SKL_SAMPLE_TYPE_INT_SIGNED = 2,
|
||||
SKL_SAMPLE_TYPE_INT_UNSIGNED = 3,
|
||||
SKL_SAMPLE_TYPE_FLOAT = 4
|
||||
};
|
||||
|
||||
enum module_pin_type {
|
||||
/* All pins of the module takes same PCM inputs or outputs
|
||||
* e.g. mixout
|
||||
*/
|
||||
SKL_PIN_TYPE_HOMOGENEOUS,
|
||||
/* All pins of the module takes different PCM inputs or outputs
|
||||
* e.g mux
|
||||
*/
|
||||
SKL_PIN_TYPE_HETEROGENEOUS,
|
||||
};
|
||||
|
||||
enum skl_module_param_type {
|
||||
SKL_PARAM_DEFAULT = 0,
|
||||
SKL_PARAM_INIT,
|
||||
SKL_PARAM_SET
|
||||
};
|
||||
|
||||
struct skl_dfw_module_pin {
|
||||
u16 module_id;
|
||||
u16 instance_id;
|
||||
|
@ -121,9 +158,15 @@ struct skl_dfw_module_fmt {
|
|||
u32 bit_depth;
|
||||
u32 valid_bit_depth;
|
||||
u32 ch_cfg;
|
||||
u32 interleaving_style;
|
||||
u32 sample_type;
|
||||
u32 ch_map;
|
||||
} __packed;
|
||||
|
||||
struct skl_dfw_module_caps {
|
||||
u32 set_params:2;
|
||||
u32 rsvd:30;
|
||||
u32 param_id;
|
||||
u32 caps_size;
|
||||
u32 caps[HDA_SST_CFG_MAX];
|
||||
};
|
||||
|
@ -131,41 +174,57 @@ struct skl_dfw_module_caps {
|
|||
struct skl_dfw_pipe {
|
||||
u8 pipe_id;
|
||||
u8 pipe_priority;
|
||||
u16 conn_type;
|
||||
u32 memory_pages;
|
||||
u16 conn_type:4;
|
||||
u16 rsvd:4;
|
||||
u16 memory_pages:8;
|
||||
} __packed;
|
||||
|
||||
struct skl_dfw_module {
|
||||
char uuid[SKL_UUID_STR_SZ];
|
||||
|
||||
u16 module_id;
|
||||
u16 instance_id;
|
||||
u32 max_mcps;
|
||||
u8 core_id;
|
||||
u8 max_in_queue;
|
||||
u8 max_out_queue;
|
||||
u8 is_loadable;
|
||||
u8 conn_type;
|
||||
u8 dev_type;
|
||||
u8 hw_conn_type;
|
||||
u8 time_slot;
|
||||
u32 mem_pages;
|
||||
u32 obs;
|
||||
u32 ibs;
|
||||
u32 params_fixup;
|
||||
u32 converter;
|
||||
u32 module_type;
|
||||
u32 vbus_id;
|
||||
u8 is_dynamic_in_pin;
|
||||
u8 is_dynamic_out_pin;
|
||||
|
||||
u32 max_in_queue:8;
|
||||
u32 max_out_queue:8;
|
||||
u32 time_slot:8;
|
||||
u32 core_id:4;
|
||||
u32 rsvd1:4;
|
||||
|
||||
u32 module_type:8;
|
||||
u32 conn_type:4;
|
||||
u32 dev_type:4;
|
||||
u32 hw_conn_type:4;
|
||||
u32 rsvd2:12;
|
||||
|
||||
u32 params_fixup:8;
|
||||
u32 converter:8;
|
||||
u32 input_pin_type:1;
|
||||
u32 output_pin_type:1;
|
||||
u32 is_dynamic_in_pin:1;
|
||||
u32 is_dynamic_out_pin:1;
|
||||
u32 is_loadable:1;
|
||||
u32 rsvd3:11;
|
||||
|
||||
struct skl_dfw_pipe pipe;
|
||||
struct skl_dfw_module_fmt in_fmt;
|
||||
struct skl_dfw_module_fmt out_fmt;
|
||||
struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE];
|
||||
struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE];
|
||||
struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
|
||||
struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
|
||||
struct skl_dfw_module_caps caps;
|
||||
} __packed;
|
||||
|
||||
struct skl_dfw_algo_data {
|
||||
u32 set_params:2;
|
||||
u32 rsvd:30;
|
||||
u32 param_id;
|
||||
u32 max;
|
||||
char *params;
|
||||
char params[0];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,7 +27,10 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "../common/sst-acpi.h"
|
||||
#include "skl.h"
|
||||
#include "skl-sst-dsp.h"
|
||||
#include "skl-sst-ipc.h"
|
||||
|
||||
/*
|
||||
* initialize the PCI registers
|
||||
|
@ -58,6 +61,49 @@ static void skl_init_pci(struct skl *skl)
|
|||
skl_update_pci_byte(skl->pci, AZX_PCIREG_TCSEL, 0x07, 0);
|
||||
}
|
||||
|
||||
static void update_pci_dword(struct pci_dev *pci,
|
||||
unsigned int reg, u32 mask, u32 val)
|
||||
{
|
||||
u32 data = 0;
|
||||
|
||||
pci_read_config_dword(pci, reg, &data);
|
||||
data &= ~mask;
|
||||
data |= (val & mask);
|
||||
pci_write_config_dword(pci, reg, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* skl_enable_miscbdcge - enable/dsiable CGCTL.MISCBDCGE bits
|
||||
*
|
||||
* @dev: device pointer
|
||||
* @enable: enable/disable flag
|
||||
*/
|
||||
static void skl_enable_miscbdcge(struct device *dev, bool enable)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
u32 val;
|
||||
|
||||
val = enable ? AZX_CGCTL_MISCBDCGE_MASK : 0;
|
||||
|
||||
update_pci_dword(pci, AZX_PCIREG_CGCTL, AZX_CGCTL_MISCBDCGE_MASK, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* While performing reset, controller may not come back properly causing
|
||||
* issues, so recommendation is to set CGCTL.MISCBDCGE to 0 then do reset
|
||||
* (init chip) and then again set CGCTL.MISCBDCGE to 1
|
||||
*/
|
||||
static int skl_init_chip(struct hdac_bus *bus, bool full_reset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
skl_enable_miscbdcge(bus->dev, false);
|
||||
ret = snd_hdac_bus_init_chip(bus, full_reset);
|
||||
skl_enable_miscbdcge(bus->dev, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* called from IRQ */
|
||||
static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr)
|
||||
{
|
||||
|
@ -130,6 +176,39 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int _skl_suspend(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
int ret;
|
||||
|
||||
snd_hdac_ext_bus_link_power_down_all(ebus);
|
||||
|
||||
ret = skl_suspend_dsp(skl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
snd_hdac_bus_stop_chip(bus);
|
||||
skl_enable_miscbdcge(bus->dev, false);
|
||||
snd_hdac_bus_enter_link_reset(bus);
|
||||
skl_enable_miscbdcge(bus->dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _skl_resume(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
skl_init_pci(skl);
|
||||
skl_init_chip(bus, true);
|
||||
|
||||
return skl_resume_dsp(skl);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* power management
|
||||
|
@ -138,26 +217,46 @@ static int skl_suspend(struct device *dev)
|
|||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
|
||||
snd_hdac_bus_stop_chip(bus);
|
||||
snd_hdac_bus_enter_link_reset(bus);
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* Do not suspend if streams which are marked ignore suspend are
|
||||
* running, we need to save the state for these and continue
|
||||
*/
|
||||
if (skl->supend_active) {
|
||||
snd_hdac_ext_bus_link_power_down_all(ebus);
|
||||
enable_irq_wake(bus->irq);
|
||||
pci_save_state(pci);
|
||||
pci_disable_device(pci);
|
||||
return 0;
|
||||
} else {
|
||||
return _skl_suspend(ebus);
|
||||
}
|
||||
}
|
||||
|
||||
static int skl_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct skl *hda = ebus_to_skl(ebus);
|
||||
int ret;
|
||||
|
||||
skl_init_pci(hda);
|
||||
/*
|
||||
* resume only when we are not in suspend active, otherwise need to
|
||||
* restore the device
|
||||
*/
|
||||
if (skl->supend_active) {
|
||||
pci_restore_state(pci);
|
||||
ret = pci_enable_device(pci);
|
||||
snd_hdac_ext_bus_link_power_up_all(ebus);
|
||||
disable_irq_wake(bus->irq);
|
||||
} else {
|
||||
ret = _skl_resume(ebus);
|
||||
}
|
||||
|
||||
snd_hdac_bus_init_chip(bus, 1);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
|
@ -167,24 +266,10 @@ static int skl_runtime_suspend(struct device *dev)
|
|||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
int ret;
|
||||
|
||||
dev_dbg(bus->dev, "in %s\n", __func__);
|
||||
|
||||
/* enable controller wake up event */
|
||||
snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
|
||||
|
||||
snd_hdac_ext_bus_link_power_down_all(ebus);
|
||||
|
||||
ret = skl_suspend_dsp(skl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
snd_hdac_bus_stop_chip(bus);
|
||||
snd_hdac_bus_enter_link_reset(bus);
|
||||
|
||||
return 0;
|
||||
return _skl_suspend(ebus);
|
||||
}
|
||||
|
||||
static int skl_runtime_resume(struct device *dev)
|
||||
|
@ -192,20 +277,10 @@ static int skl_runtime_resume(struct device *dev)
|
|||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
|
||||
struct hdac_bus *bus = ebus_to_hbus(ebus);
|
||||
struct skl *skl = ebus_to_skl(ebus);
|
||||
int status;
|
||||
|
||||
dev_dbg(bus->dev, "in %s\n", __func__);
|
||||
|
||||
/* Read STATESTS before controller reset */
|
||||
status = snd_hdac_chip_readw(bus, STATESTS);
|
||||
|
||||
skl_init_pci(skl);
|
||||
snd_hdac_bus_init_chip(bus, true);
|
||||
/* disable controller Wake Up event */
|
||||
snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
|
||||
|
||||
return skl_resume_dsp(skl);
|
||||
return _skl_resume(ebus);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
@ -242,6 +317,43 @@ static int skl_free(struct hdac_ext_bus *ebus)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int skl_machine_device_register(struct skl *skl, void *driver_data)
|
||||
{
|
||||
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
|
||||
struct platform_device *pdev;
|
||||
struct sst_acpi_mach *mach = driver_data;
|
||||
int ret;
|
||||
|
||||
mach = sst_acpi_find_machine(mach);
|
||||
if (mach == NULL) {
|
||||
dev_err(bus->dev, "No matching machine driver found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
skl->fw_name = mach->fw_filename;
|
||||
|
||||
pdev = platform_device_alloc(mach->drv_name, -1);
|
||||
if (pdev == NULL) {
|
||||
dev_err(bus->dev, "platform device alloc failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
if (ret) {
|
||||
dev_err(bus->dev, "failed to add machine device\n");
|
||||
platform_device_put(pdev);
|
||||
return -EIO;
|
||||
}
|
||||
skl->i2s_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void skl_machine_device_unregister(struct skl *skl)
|
||||
{
|
||||
if (skl->i2s_dev)
|
||||
platform_device_unregister(skl->i2s_dev);
|
||||
}
|
||||
|
||||
static int skl_dmic_device_register(struct skl *skl)
|
||||
{
|
||||
struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
|
||||
|
@ -321,7 +433,7 @@ static int skl_codec_create(struct hdac_ext_bus *ebus)
|
|||
* back to the sanity state.
|
||||
*/
|
||||
snd_hdac_bus_stop_chip(bus);
|
||||
snd_hdac_bus_init_chip(bus, true);
|
||||
skl_init_chip(bus, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -431,12 +543,11 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
|
|||
/* initialize chip */
|
||||
skl_init_pci(skl);
|
||||
|
||||
snd_hdac_bus_init_chip(bus, true);
|
||||
skl_init_chip(bus, true);
|
||||
|
||||
/* codec detection */
|
||||
if (!bus->codec_mask) {
|
||||
dev_err(bus->dev, "no codecs found!\n");
|
||||
return -ENODEV;
|
||||
dev_info(bus->dev, "no hda codecs found!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -471,11 +582,18 @@ static int skl_probe(struct pci_dev *pci,
|
|||
|
||||
/* check if dsp is there */
|
||||
if (ebus->ppcap) {
|
||||
err = skl_machine_device_register(skl,
|
||||
(void *)pci_id->driver_data);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
err = skl_init_dsp(skl);
|
||||
if (err < 0) {
|
||||
dev_dbg(bus->dev, "error failed to register dsp\n");
|
||||
goto out_free;
|
||||
goto out_mach_free;
|
||||
}
|
||||
skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
|
||||
|
||||
}
|
||||
if (ebus->mlcap)
|
||||
snd_hdac_ext_bus_get_ml_capabilities(ebus);
|
||||
|
@ -509,6 +627,8 @@ out_dmic_free:
|
|||
skl_dmic_device_unregister(skl);
|
||||
out_dsp_free:
|
||||
skl_free_dsp(skl);
|
||||
out_mach_free:
|
||||
skl_machine_device_unregister(skl);
|
||||
out_free:
|
||||
skl->init_failed = 1;
|
||||
skl_free(ebus);
|
||||
|
@ -529,15 +649,26 @@ static void skl_remove(struct pci_dev *pci)
|
|||
pci_dev_put(pci);
|
||||
skl_platform_unregister(&pci->dev);
|
||||
skl_free_dsp(skl);
|
||||
skl_machine_device_unregister(skl);
|
||||
skl_dmic_device_unregister(skl);
|
||||
skl_free(ebus);
|
||||
dev_set_drvdata(&pci->dev, NULL);
|
||||
}
|
||||
|
||||
static struct sst_acpi_mach sst_skl_devdata[] = {
|
||||
{ "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL },
|
||||
{ "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin",
|
||||
NULL, NULL, NULL },
|
||||
{ "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin",
|
||||
NULL, NULL, NULL },
|
||||
{}
|
||||
};
|
||||
|
||||
/* PCI IDs */
|
||||
static const struct pci_device_id skl_ids[] = {
|
||||
/* Sunrise Point-LP */
|
||||
{ PCI_DEVICE(0x8086, 0x9d70), 0},
|
||||
{ PCI_DEVICE(0x8086, 0x9d70),
|
||||
.driver_data = (unsigned long)&sst_skl_devdata},
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, skl_ids);
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
|
||||
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
|
||||
|
||||
#define AZX_PCIREG_CGCTL 0x48
|
||||
#define AZX_CGCTL_MISCBDCGE_MASK (1 << 6)
|
||||
|
||||
struct skl_dsp_resource {
|
||||
u32 max_mcps;
|
||||
u32 max_mem;
|
||||
|
@ -61,6 +64,7 @@ struct skl {
|
|||
|
||||
unsigned int init_failed:1; /* delayed init failed */
|
||||
struct platform_device *dmic_dev;
|
||||
struct platform_device *i2s_dev;
|
||||
|
||||
void *nhlt; /* nhlt ptr */
|
||||
struct skl_sst *skl_sst; /* sst skl ctx */
|
||||
|
@ -68,8 +72,10 @@ struct skl {
|
|||
struct skl_dsp_resource resource;
|
||||
struct list_head ppl_list;
|
||||
struct list_head dapm_path_list;
|
||||
|
||||
const char *fw_name;
|
||||
const struct firmware *tplg;
|
||||
|
||||
int supend_active;
|
||||
};
|
||||
|
||||
#define skl_to_ebus(s) (&(s)->ebus)
|
||||
|
|
|
@ -81,8 +81,12 @@ static int rear_amp_power(struct snd_soc_codec *codec, int power)
|
|||
static int rear_amp_event(struct snd_soc_dapm_widget *widget,
|
||||
struct snd_kcontrol *kctl, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = widget->dapm->card->rtd[0].codec;
|
||||
struct snd_soc_card *card = widget->dapm->card;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_codec *codec;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec = rtd->codec;
|
||||
return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event));
|
||||
}
|
||||
|
||||
|
|
|
@ -58,11 +58,16 @@ static int bells_set_bias_level(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct snd_soc_codec *codec;
|
||||
struct bells_drvdata *bells = card->drvdata;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
codec = codec_dai->codec;
|
||||
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -99,11 +104,16 @@ static int bells_set_bias_level_post(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct snd_soc_codec *codec;
|
||||
struct bells_drvdata *bells = card->drvdata;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
codec = codec_dai->codec;
|
||||
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -137,14 +147,22 @@ static int bells_set_bias_level_post(struct snd_soc_card *card,
|
|||
static int bells_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct bells_drvdata *bells = card->drvdata;
|
||||
struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec;
|
||||
struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec;
|
||||
struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_codec *wm0010;
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_dai *aif1_dai;
|
||||
struct snd_soc_dai *aif2_dai;
|
||||
struct snd_soc_dai *aif3_dai;
|
||||
struct snd_soc_dai *wm9081_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name);
|
||||
wm0010 = rtd->codec;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
|
||||
codec = rtd->codec;
|
||||
aif1_dai = rtd->codec_dai;
|
||||
|
||||
ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
|
||||
ARIZONA_CLK_SRC_FLL1,
|
||||
bells->sysclk_rate,
|
||||
|
@ -181,7 +199,8 @@ static int bells_late_probe(struct snd_soc_card *card)
|
|||
return ret;
|
||||
}
|
||||
|
||||
aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai;
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_CP].name);
|
||||
aif2_dai = rtd->cpu_dai;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
|
||||
if (ret != 0) {
|
||||
|
@ -192,8 +211,9 @@ static int bells_late_probe(struct snd_soc_card *card)
|
|||
if (card->num_rtd == DAI_CODEC_SUB)
|
||||
return 0;
|
||||
|
||||
aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai;
|
||||
wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai;
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_SUB].name);
|
||||
aif3_dai = rtd->cpu_dai;
|
||||
wm9081_dai = rtd->codec_dai;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
|
||||
if (ret != 0) {
|
||||
|
|
|
@ -23,9 +23,13 @@ static int littlemill_set_bias_level(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *aif1_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
aif1_dai = rtd->codec_dai;
|
||||
|
||||
if (dapm->dev != aif1_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -66,9 +70,13 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *aif1_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
aif1_dai = rtd->codec_dai;
|
||||
|
||||
if (dapm->dev != aif1_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -168,9 +176,13 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w,
|
|||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_card *card = w->dapm->card;
|
||||
struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *aif2_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
|
||||
aif2_dai = rtd->cpu_dai;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2,
|
||||
|
@ -245,11 +257,19 @@ static struct snd_soc_jack littlemill_headset;
|
|||
|
||||
static int littlemill_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_codec *codec = card->rtd[0].codec;
|
||||
struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_dai *aif1_dai;
|
||||
struct snd_soc_dai *aif2_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec = rtd->codec;
|
||||
aif1_dai = rtd->codec_dai;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
|
||||
aif2_dai = rtd->cpu_dai;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
|
||||
32768, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -25,10 +25,15 @@ static struct snd_soc_dai_link odroidx2_dai[];
|
|||
|
||||
static int odroidx2_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct snd_soc_dai *cpu_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
cpu_dai = rtd->cpu_dai;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
|
||||
SND_SOC_CLOCK_IN);
|
||||
|
||||
|
|
|
@ -35,10 +35,15 @@ static struct snd_soc_dai_link snow_dai[] = {
|
|||
|
||||
static int snow_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct snd_soc_dai *cpu_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
cpu_dai = rtd->cpu_dai;
|
||||
|
||||
/* Set the MCLK rate for the codec */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
|
||||
FIN_PLL_RATE, SND_SOC_CLOCK_IN);
|
||||
|
|
|
@ -25,9 +25,13 @@ static int speyside_set_bias_level(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -57,9 +61,13 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -23,9 +23,13 @@ static int tobermory_set_bias_level(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -62,9 +66,13 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_context *dapm,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec_dai = rtd->codec_dai;
|
||||
|
||||
if (dapm->dev != codec_dai->dev)
|
||||
return 0;
|
||||
|
||||
|
@ -170,10 +178,15 @@ static struct snd_soc_jack_pin tobermory_headset_pins[] = {
|
|||
|
||||
static int tobermory_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_codec *codec = card->rtd[0].codec;
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
codec = rtd->codec;
|
||||
codec_dai = rtd->codec_dai;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
|
||||
32768, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -1040,7 +1040,7 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
|
|||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = name,
|
||||
.info = rsnd_kctrl_info,
|
||||
.index = rtd - soc_card->rtd,
|
||||
.index = rtd->num,
|
||||
.get = rsnd_kctrl_get,
|
||||
.put = rsnd_kctrl_put,
|
||||
.private_value = (unsigned long)cfg,
|
||||
|
|
|
@ -75,7 +75,7 @@ static int rsrc_card_startup(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct rsrc_card_dai *dai_props =
|
||||
rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
|
||||
rsrc_priv_to_props(priv, rtd->num);
|
||||
|
||||
return clk_prepare_enable(dai_props->clk);
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct rsrc_card_dai *dai_props =
|
||||
rsrc_priv_to_props(priv, rtd - rtd->card->rtd);
|
||||
rsrc_priv_to_props(priv, rtd->num);
|
||||
|
||||
clk_disable_unprepare(dai_props->clk);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
struct snd_soc_dai *dai;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct rsrc_card_dai *dai_props;
|
||||
int num = rtd - rtd->card->rtd;
|
||||
int num = rtd->num;
|
||||
int ret;
|
||||
|
||||
dai_link = rsrc_priv_to_link(priv, num);
|
||||
|
|
|
@ -537,26 +537,75 @@ static inline void snd_soc_debugfs_exit(void)
|
|||
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
|
||||
const char *dai_link, int stream)
|
||||
{
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
if (card->rtd[i].dai_link->no_pcm &&
|
||||
!strcmp(card->rtd[i].dai_link->name, dai_link))
|
||||
return card->rtd[i].pcm->streams[stream].substream;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (rtd->dai_link->no_pcm &&
|
||||
!strcmp(rtd->dai_link->name, dai_link))
|
||||
return rtd->pcm->streams[stream].substream;
|
||||
}
|
||||
dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
|
||||
|
||||
static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
|
||||
struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
|
||||
if (!rtd)
|
||||
return NULL;
|
||||
|
||||
rtd->card = card;
|
||||
rtd->dai_link = dai_link;
|
||||
rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) *
|
||||
dai_link->num_codecs,
|
||||
GFP_KERNEL);
|
||||
if (!rtd->codec_dais) {
|
||||
kfree(rtd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rtd;
|
||||
}
|
||||
|
||||
static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
if (rtd && rtd->codec_dais)
|
||||
kfree(rtd->codec_dais);
|
||||
kfree(rtd);
|
||||
}
|
||||
|
||||
static void soc_add_pcm_runtime(struct snd_soc_card *card,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
list_add_tail(&rtd->list, &card->rtd_list);
|
||||
rtd->num = card->num_rtd;
|
||||
card->num_rtd++;
|
||||
}
|
||||
|
||||
static void soc_remove_pcm_runtimes(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd, *_rtd;
|
||||
|
||||
list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) {
|
||||
list_del(&rtd->list);
|
||||
soc_free_pcm_runtime(rtd);
|
||||
}
|
||||
|
||||
card->num_rtd = 0;
|
||||
}
|
||||
|
||||
struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
|
||||
const char *dai_link)
|
||||
{
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
if (!strcmp(card->rtd[i].dai_link->name, dai_link))
|
||||
return &card->rtd[i];
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (!strcmp(rtd->dai_link->name, dai_link))
|
||||
return rtd;
|
||||
}
|
||||
dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link);
|
||||
return NULL;
|
||||
|
@ -578,7 +627,8 @@ int snd_soc_suspend(struct device *dev)
|
|||
{
|
||||
struct snd_soc_card *card = dev_get_drvdata(dev);
|
||||
struct snd_soc_codec *codec;
|
||||
int i, j;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
int i;
|
||||
|
||||
/* If the card is not initialized yet there is nothing to do */
|
||||
if (!card->instantiated)
|
||||
|
@ -595,13 +645,13 @@ int snd_soc_suspend(struct device *dev)
|
|||
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
|
||||
|
||||
/* mute any active DACs */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < card->rtd[i].num_codecs; j++) {
|
||||
struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
struct snd_soc_dai *dai = rtd->codec_dais[i];
|
||||
struct snd_soc_dai_driver *drv = dai->driver;
|
||||
|
||||
if (drv->ops->digital_mute && dai->playback_active)
|
||||
|
@ -610,20 +660,20 @@ int snd_soc_suspend(struct device *dev)
|
|||
}
|
||||
|
||||
/* suspend all pcms */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
snd_pcm_suspend_all(card->rtd[i].pcm);
|
||||
snd_pcm_suspend_all(rtd->pcm);
|
||||
}
|
||||
|
||||
if (card->suspend_pre)
|
||||
card->suspend_pre(card);
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
|
||||
|
@ -631,19 +681,19 @@ int snd_soc_suspend(struct device *dev)
|
|||
}
|
||||
|
||||
/* close any waiting streams */
|
||||
for (i = 0; i < card->num_rtd; i++)
|
||||
flush_delayed_work(&card->rtd[i].delayed_work);
|
||||
list_for_each_entry(rtd, &card->rtd_list, list)
|
||||
flush_delayed_work(&rtd->delayed_work);
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
snd_soc_dapm_stream_event(&card->rtd[i],
|
||||
snd_soc_dapm_stream_event(rtd,
|
||||
SNDRV_PCM_STREAM_PLAYBACK,
|
||||
SND_SOC_DAPM_STREAM_SUSPEND);
|
||||
|
||||
snd_soc_dapm_stream_event(&card->rtd[i],
|
||||
snd_soc_dapm_stream_event(rtd,
|
||||
SNDRV_PCM_STREAM_CAPTURE,
|
||||
SND_SOC_DAPM_STREAM_SUSPEND);
|
||||
}
|
||||
|
@ -690,10 +740,10 @@ int snd_soc_suspend(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
|
||||
|
@ -717,8 +767,9 @@ static void soc_resume_deferred(struct work_struct *work)
|
|||
{
|
||||
struct snd_soc_card *card =
|
||||
container_of(work, struct snd_soc_card, deferred_resume_work);
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_codec *codec;
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
|
||||
* so userspace apps are blocked from touching us
|
||||
|
@ -733,10 +784,10 @@ static void soc_resume_deferred(struct work_struct *work)
|
|||
card->resume_pre(card);
|
||||
|
||||
/* resume control bus DAIs */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
|
||||
|
@ -751,28 +802,28 @@ static void soc_resume_deferred(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
snd_soc_dapm_stream_event(&card->rtd[i],
|
||||
snd_soc_dapm_stream_event(rtd,
|
||||
SNDRV_PCM_STREAM_PLAYBACK,
|
||||
SND_SOC_DAPM_STREAM_RESUME);
|
||||
|
||||
snd_soc_dapm_stream_event(&card->rtd[i],
|
||||
snd_soc_dapm_stream_event(rtd,
|
||||
SNDRV_PCM_STREAM_CAPTURE,
|
||||
SND_SOC_DAPM_STREAM_RESUME);
|
||||
}
|
||||
|
||||
/* unmute any active DACs */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < card->rtd[i].num_codecs; j++) {
|
||||
struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
struct snd_soc_dai *dai = rtd->codec_dais[i];
|
||||
struct snd_soc_dai_driver *drv = dai->driver;
|
||||
|
||||
if (drv->ops->digital_mute && dai->playback_active)
|
||||
|
@ -780,10 +831,10 @@ static void soc_resume_deferred(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
if (card->rtd[i].dai_link->ignore_suspend)
|
||||
if (rtd->dai_link->ignore_suspend)
|
||||
continue;
|
||||
|
||||
if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
|
||||
|
@ -808,15 +859,14 @@ int snd_soc_resume(struct device *dev)
|
|||
{
|
||||
struct snd_soc_card *card = dev_get_drvdata(dev);
|
||||
bool bus_control = false;
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
/* If the card is not initialized yet there is nothing to do */
|
||||
if (!card->instantiated)
|
||||
return 0;
|
||||
|
||||
/* activate pins from sleep state */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai **codec_dais = rtd->codec_dais;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
int j;
|
||||
|
@ -837,8 +887,8 @@ int snd_soc_resume(struct device *dev)
|
|||
* have that problem and may take a substantial amount of time to resume
|
||||
* due to I/O costs and anti-pop so handle them out of line.
|
||||
*/
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
bus_control |= cpu_dai->driver->bus_control;
|
||||
}
|
||||
if (bus_control) {
|
||||
|
@ -913,16 +963,20 @@ static struct snd_soc_dai *snd_soc_find_dai(
|
|||
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
|
||||
struct snd_soc_dai_link_component cpu_dai_component;
|
||||
struct snd_soc_dai **codec_dais = rtd->codec_dais;
|
||||
struct snd_soc_dai **codec_dais;
|
||||
struct snd_soc_platform *platform;
|
||||
const char *platform_name;
|
||||
int i;
|
||||
|
||||
dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
|
||||
|
||||
rtd = soc_new_pcm_runtime(card, dai_link);
|
||||
if (!rtd)
|
||||
return -ENOMEM;
|
||||
|
||||
cpu_dai_component.name = dai_link->cpu_name;
|
||||
cpu_dai_component.of_node = dai_link->cpu_of_node;
|
||||
cpu_dai_component.dai_name = dai_link->cpu_dai_name;
|
||||
|
@ -930,18 +984,19 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
|||
if (!rtd->cpu_dai) {
|
||||
dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
|
||||
dai_link->cpu_dai_name);
|
||||
return -EPROBE_DEFER;
|
||||
goto _err_defer;
|
||||
}
|
||||
|
||||
rtd->num_codecs = dai_link->num_codecs;
|
||||
|
||||
/* Find CODEC from registered CODECs */
|
||||
codec_dais = rtd->codec_dais;
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
codec_dais[i] = snd_soc_find_dai(&codecs[i]);
|
||||
if (!codec_dais[i]) {
|
||||
dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
|
||||
codecs[i].dai_name);
|
||||
return -EPROBE_DEFER;
|
||||
goto _err_defer;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -973,9 +1028,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
|||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
card->num_rtd++;
|
||||
|
||||
soc_add_pcm_runtime(card, rtd);
|
||||
return 0;
|
||||
|
||||
_err_defer:
|
||||
soc_free_pcm_runtime(rtd);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
static void soc_remove_component(struct snd_soc_component *component)
|
||||
|
@ -1014,9 +1072,9 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order)
|
|||
}
|
||||
}
|
||||
|
||||
static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
|
||||
static void soc_remove_link_dais(struct snd_soc_card *card,
|
||||
struct snd_soc_pcm_runtime *rtd, int order)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
||||
int i;
|
||||
|
||||
/* unregister the rtd device */
|
||||
|
@ -1032,10 +1090,9 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
|
|||
soc_remove_dai(rtd->cpu_dai, order);
|
||||
}
|
||||
|
||||
static void soc_remove_link_components(struct snd_soc_card *card, int num,
|
||||
int order)
|
||||
static void soc_remove_link_components(struct snd_soc_card *card,
|
||||
struct snd_soc_pcm_runtime *rtd, int order)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_platform *platform = rtd->platform;
|
||||
struct snd_soc_component *component;
|
||||
|
@ -1061,21 +1118,20 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
|
|||
|
||||
static void soc_remove_dai_links(struct snd_soc_card *card)
|
||||
{
|
||||
int dai, order;
|
||||
int order;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
||||
order++) {
|
||||
for (dai = 0; dai < card->num_rtd; dai++)
|
||||
soc_remove_link_dais(card, dai, order);
|
||||
list_for_each_entry(rtd, &card->rtd_list, list)
|
||||
soc_remove_link_dais(card, rtd, order);
|
||||
}
|
||||
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
||||
order++) {
|
||||
for (dai = 0; dai < card->num_rtd; dai++)
|
||||
soc_remove_link_components(card, dai, order);
|
||||
list_for_each_entry(rtd, &card->rtd_list, list)
|
||||
soc_remove_link_components(card, rtd, order);
|
||||
}
|
||||
|
||||
card->num_rtd = 0;
|
||||
}
|
||||
|
||||
static void soc_set_name_prefix(struct snd_soc_card *card,
|
||||
|
@ -1220,10 +1276,10 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int soc_probe_link_components(struct snd_soc_card *card, int num,
|
||||
static int soc_probe_link_components(struct snd_soc_card *card,
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
int order)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
||||
struct snd_soc_platform *platform = rtd->platform;
|
||||
struct snd_soc_component *component;
|
||||
int i, ret;
|
||||
|
@ -1319,15 +1375,15 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
||||
static int soc_probe_link_dais(struct snd_soc_card *card,
|
||||
struct snd_soc_pcm_runtime *rtd, int order)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
||||
struct snd_soc_dai_link *dai_link = rtd->dai_link;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
int i, ret;
|
||||
|
||||
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
|
||||
card->name, num, order);
|
||||
card->name, rtd->num, order);
|
||||
|
||||
/* set default power off timeout */
|
||||
rtd->pmdown_time = pmdown_time;
|
||||
|
@ -1372,7 +1428,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
|||
|
||||
if (cpu_dai->driver->compress_new) {
|
||||
/*create compress_device"*/
|
||||
ret = cpu_dai->driver->compress_new(rtd, num);
|
||||
ret = cpu_dai->driver->compress_new(rtd, rtd->num);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "ASoC: can't create compress %s\n",
|
||||
dai_link->stream_name);
|
||||
|
@ -1382,7 +1438,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
|||
|
||||
if (!dai_link->params) {
|
||||
/* create the pcm */
|
||||
ret = soc_new_pcm(rtd, num);
|
||||
ret = soc_new_pcm(rtd, rtd->num);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
|
||||
dai_link->stream_name, ret);
|
||||
|
@ -1552,6 +1608,7 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
|
|||
static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
int ret, i, order;
|
||||
|
||||
mutex_lock(&client_mutex);
|
||||
|
@ -1624,8 +1681,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|||
/* probe all components used by DAI links on this card */
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
||||
order++) {
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
ret = soc_probe_link_components(card, i, order);
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
ret = soc_probe_link_components(card, rtd, order);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: failed to instantiate card %d\n",
|
||||
|
@ -1638,8 +1695,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|||
/* probe all DAI links on this card */
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
||||
order++) {
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
ret = soc_probe_link_dais(card, i, order);
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
ret = soc_probe_link_dais(card, rtd, order);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: failed to instantiate card %d\n",
|
||||
|
@ -1733,6 +1790,7 @@ card_probe_error:
|
|||
snd_card_free(card->snd_card);
|
||||
|
||||
base_error:
|
||||
soc_remove_pcm_runtimes(card);
|
||||
mutex_unlock(&card->mutex);
|
||||
mutex_unlock(&client_mutex);
|
||||
|
||||
|
@ -1763,13 +1821,12 @@ static int soc_probe(struct platform_device *pdev)
|
|||
|
||||
static int soc_cleanup_card_resources(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
int i;
|
||||
|
||||
/* make sure any delayed work runs */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
||||
list_for_each_entry(rtd, &card->rtd_list, list)
|
||||
flush_delayed_work(&rtd->delayed_work);
|
||||
}
|
||||
|
||||
/* remove auxiliary devices */
|
||||
for (i = 0; i < card->num_aux_devs; i++)
|
||||
|
@ -1777,6 +1834,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
|
|||
|
||||
/* remove and free each DAI */
|
||||
soc_remove_dai_links(card);
|
||||
soc_remove_pcm_runtimes(card);
|
||||
|
||||
soc_cleanup_card_debugfs(card);
|
||||
|
||||
|
@ -1803,29 +1861,26 @@ static int soc_remove(struct platform_device *pdev)
|
|||
int snd_soc_poweroff(struct device *dev)
|
||||
{
|
||||
struct snd_soc_card *card = dev_get_drvdata(dev);
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
if (!card->instantiated)
|
||||
return 0;
|
||||
|
||||
/* Flush out pmdown_time work - we actually do want to run it
|
||||
* now, we're shutting down so no imminent restart. */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
||||
list_for_each_entry(rtd, &card->rtd_list, list)
|
||||
flush_delayed_work(&rtd->delayed_work);
|
||||
}
|
||||
|
||||
snd_soc_dapm_shutdown(card);
|
||||
|
||||
/* deactivate pins to sleep state */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
int j;
|
||||
int i;
|
||||
|
||||
pinctrl_pm_select_sleep_state(cpu_dai->dev);
|
||||
for (j = 0; j < rtd->num_codecs; j++) {
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
|
||||
for (i = 0; i < rtd->num_codecs; i++) {
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
|
||||
pinctrl_pm_select_sleep_state(codec_dai->dev);
|
||||
}
|
||||
}
|
||||
|
@ -2337,6 +2392,7 @@ static int snd_soc_init_multicodec(struct snd_soc_card *card,
|
|||
int snd_soc_register_card(struct snd_soc_card *card)
|
||||
{
|
||||
int i, j, ret;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
if (!card->name || !card->dev)
|
||||
return -EINVAL;
|
||||
|
@ -2408,25 +2464,15 @@ int snd_soc_register_card(struct snd_soc_card *card)
|
|||
|
||||
snd_soc_initialize_card_lists(card);
|
||||
|
||||
card->rtd = devm_kzalloc(card->dev,
|
||||
sizeof(struct snd_soc_pcm_runtime) *
|
||||
(card->num_links + card->num_aux_devs),
|
||||
GFP_KERNEL);
|
||||
if (card->rtd == NULL)
|
||||
return -ENOMEM;
|
||||
INIT_LIST_HEAD(&card->rtd_list);
|
||||
card->num_rtd = 0;
|
||||
card->rtd_aux = &card->rtd[card->num_links];
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
card->rtd[i].card = card;
|
||||
card->rtd[i].dai_link = &card->dai_link[i];
|
||||
card->rtd[i].codec_dais = devm_kzalloc(card->dev,
|
||||
sizeof(struct snd_soc_dai *) *
|
||||
(card->rtd[i].dai_link->num_codecs),
|
||||
GFP_KERNEL);
|
||||
if (card->rtd[i].codec_dais == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
card->rtd_aux = devm_kzalloc(card->dev,
|
||||
sizeof(struct snd_soc_pcm_runtime) *
|
||||
card->num_aux_devs,
|
||||
GFP_KERNEL);
|
||||
if (card->rtd_aux == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < card->num_aux_devs; i++)
|
||||
card->rtd_aux[i].card = card;
|
||||
|
@ -2442,8 +2488,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
|
|||
return ret;
|
||||
|
||||
/* deactivate pins to sleep state */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
int j;
|
||||
|
||||
|
|
|
@ -3358,6 +3358,11 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
|
|||
w->is_ep = SND_SOC_DAPM_EP_SOURCE;
|
||||
w->power_check = dapm_always_on_check_power;
|
||||
break;
|
||||
case snd_soc_dapm_sink:
|
||||
w->is_ep = SND_SOC_DAPM_EP_SINK;
|
||||
w->power_check = dapm_always_on_check_power;
|
||||
break;
|
||||
|
||||
case snd_soc_dapm_mux:
|
||||
case snd_soc_dapm_demux:
|
||||
case snd_soc_dapm_switch:
|
||||
|
@ -3900,13 +3905,10 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
|
|||
|
||||
void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = card->rtd;
|
||||
int i;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
/* for each BE DAI link... */
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
rtd = &card->rtd[i];
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
/*
|
||||
* dynamic FE links have no fixed DAI mapping.
|
||||
* CODEC<->CODEC links have no direct connection.
|
||||
|
|
|
@ -1213,11 +1213,10 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_widget *widget, int stream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *be;
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
be = &card->rtd[i];
|
||||
list_for_each_entry(be, &card->rtd_list, list) {
|
||||
|
||||
if (!be->dai_link->no_pcm)
|
||||
continue;
|
||||
|
@ -1225,16 +1224,15 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
|
|||
if (be->cpu_dai->playback_widget == widget)
|
||||
return be;
|
||||
|
||||
for (j = 0; j < be->num_codecs; j++) {
|
||||
struct snd_soc_dai *dai = be->codec_dais[j];
|
||||
for (i = 0; i < be->num_codecs; i++) {
|
||||
struct snd_soc_dai *dai = be->codec_dais[i];
|
||||
if (dai->playback_widget == widget)
|
||||
return be;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
be = &card->rtd[i];
|
||||
list_for_each_entry(be, &card->rtd_list, list) {
|
||||
|
||||
if (!be->dai_link->no_pcm)
|
||||
continue;
|
||||
|
@ -1242,8 +1240,8 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
|
|||
if (be->cpu_dai->capture_widget == widget)
|
||||
return be;
|
||||
|
||||
for (j = 0; j < be->num_codecs; j++) {
|
||||
struct snd_soc_dai *dai = be->codec_dais[j];
|
||||
for (i = 0; i < be->num_codecs; i++) {
|
||||
struct snd_soc_dai *dai = be->codec_dais[i];
|
||||
if (dai->capture_widget == widget)
|
||||
return be;
|
||||
}
|
||||
|
@ -2343,12 +2341,12 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
|
|||
*/
|
||||
int soc_dpcm_runtime_update(struct snd_soc_card *card)
|
||||
{
|
||||
int i, old, new, paths;
|
||||
struct snd_soc_pcm_runtime *fe;
|
||||
int old, new, paths;
|
||||
|
||||
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
list_for_each_entry(fe, &card->rtd_list, list) {
|
||||
struct snd_soc_dapm_widget_list *list;
|
||||
struct snd_soc_pcm_runtime *fe = &card->rtd[i];
|
||||
|
||||
/* make sure link is FE */
|
||||
if (!fe->dai_link->dynamic)
|
||||
|
|
|
@ -199,7 +199,8 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
|
|||
|
||||
static int tegra_wm8903_remove(struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = &(card->rtd[0]);
|
||||
struct snd_soc_pcm_runtime *rtd =
|
||||
snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
|
||||
|
|
Загрузка…
Ссылка в новой задаче