Merge remote-tracking branch 'asoc/topic/hdmi' into asoc-next
This commit is contained in:
commit
515511a792
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* hdmi-codec.h - HDMI Codec driver API
|
||||
*
|
||||
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Jyri Sarha <jsarha@ti.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __HDMI_CODEC_H__
|
||||
#define __HDMI_CODEC_H__
|
||||
|
||||
#include <linux/hdmi.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <uapi/sound/asound.h>
|
||||
|
||||
/*
|
||||
* Protocol between ASoC cpu-dai and HDMI-encoder
|
||||
*/
|
||||
struct hdmi_codec_daifmt {
|
||||
enum {
|
||||
HDMI_I2S,
|
||||
HDMI_RIGHT_J,
|
||||
HDMI_LEFT_J,
|
||||
HDMI_DSP_A,
|
||||
HDMI_DSP_B,
|
||||
HDMI_AC97,
|
||||
HDMI_SPDIF,
|
||||
} fmt;
|
||||
int bit_clk_inv:1;
|
||||
int frame_clk_inv:1;
|
||||
int bit_clk_master:1;
|
||||
int frame_clk_master:1;
|
||||
};
|
||||
|
||||
/*
|
||||
* HDMI audio parameters
|
||||
*/
|
||||
struct hdmi_codec_params {
|
||||
struct hdmi_audio_infoframe cea;
|
||||
struct snd_aes_iec958 iec;
|
||||
int sample_rate;
|
||||
int sample_width;
|
||||
int channels;
|
||||
};
|
||||
|
||||
struct hdmi_codec_ops {
|
||||
/*
|
||||
* Called when ASoC starts an audio stream setup.
|
||||
* Optional
|
||||
*/
|
||||
int (*audio_startup)(struct device *dev);
|
||||
|
||||
/*
|
||||
* Configures HDMI-encoder for audio stream.
|
||||
* Mandatory
|
||||
*/
|
||||
int (*hw_params)(struct device *dev,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms);
|
||||
|
||||
/*
|
||||
* Shuts down the audio stream.
|
||||
* Mandatory
|
||||
*/
|
||||
void (*audio_shutdown)(struct device *dev);
|
||||
|
||||
/*
|
||||
* Mute/unmute HDMI audio stream.
|
||||
* Optional
|
||||
*/
|
||||
int (*digital_mute)(struct device *dev, bool enable);
|
||||
|
||||
/*
|
||||
* Provides EDID-Like-Data from connected HDMI device.
|
||||
* Optional
|
||||
*/
|
||||
int (*get_eld)(struct device *dev, uint8_t *buf, size_t len);
|
||||
};
|
||||
|
||||
/* HDMI codec initalization data */
|
||||
struct hdmi_codec_pdata {
|
||||
const struct hdmi_codec_ops *ops;
|
||||
uint i2s:1;
|
||||
uint spdif:1;
|
||||
int max_i2s_channels;
|
||||
};
|
||||
|
||||
#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
|
||||
|
||||
#endif /* __HDMI_CODEC_H__ */
|
|
@ -6,4 +6,6 @@
|
|||
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
size_t len);
|
||||
|
||||
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
|
||||
u8 *cs, size_t len);
|
||||
#endif
|
||||
|
|
|
@ -9,30 +9,18 @@
|
|||
#include <linux/types.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/pcm_iec958.h>
|
||||
|
||||
/**
|
||||
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
|
||||
* @runtime: pcm runtime structure with ->rate filled in
|
||||
* @cs: channel status buffer, at least four bytes
|
||||
* @len: length of channel status buffer
|
||||
*
|
||||
* Create the consumer format channel status data in @cs of maximum size
|
||||
* @len corresponding to the parameters of the PCM runtime @runtime.
|
||||
*
|
||||
* Drivers may wish to tweak the contents of the buffer after creation.
|
||||
*
|
||||
* Returns: length of buffer, or negative error code if something failed.
|
||||
*/
|
||||
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
size_t len)
|
||||
static int create_iec958_consumer(uint rate, uint sample_width,
|
||||
u8 *cs, size_t len)
|
||||
{
|
||||
unsigned int fs, ws;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
switch (runtime->rate) {
|
||||
switch (rate) {
|
||||
case 32000:
|
||||
fs = IEC958_AES3_CON_FS_32000;
|
||||
break;
|
||||
|
@ -59,7 +47,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
|||
}
|
||||
|
||||
if (len > 4) {
|
||||
switch (snd_pcm_format_width(runtime->format)) {
|
||||
switch (sample_width) {
|
||||
case 16:
|
||||
ws = IEC958_AES4_CON_WORDLEN_20_16;
|
||||
break;
|
||||
|
@ -71,6 +59,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
|||
IEC958_AES4_CON_MAX_WORDLEN_24;
|
||||
break;
|
||||
case 24:
|
||||
case 32: /* Assume 24-bit width for 32-bit samples. */
|
||||
ws = IEC958_AES4_CON_WORDLEN_24_20 |
|
||||
IEC958_AES4_CON_MAX_WORDLEN_24;
|
||||
break;
|
||||
|
@ -92,4 +81,46 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
|||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
|
||||
* @runtime: pcm runtime structure with ->rate filled in
|
||||
* @cs: channel status buffer, at least four bytes
|
||||
* @len: length of channel status buffer
|
||||
*
|
||||
* Create the consumer format channel status data in @cs of maximum size
|
||||
* @len corresponding to the parameters of the PCM runtime @runtime.
|
||||
*
|
||||
* Drivers may wish to tweak the contents of the buffer after creation.
|
||||
*
|
||||
* Returns: length of buffer, or negative error code if something failed.
|
||||
*/
|
||||
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
size_t len)
|
||||
{
|
||||
return create_iec958_consumer(runtime->rate,
|
||||
snd_pcm_format_width(runtime->format),
|
||||
cs, len);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
|
||||
|
||||
/**
|
||||
* snd_pcm_create_iec958_consumer_hw_params - create IEC958 channel status
|
||||
* @hw_params: the hw_params instance for extracting rate and sample format
|
||||
* @cs: channel status buffer, at least four bytes
|
||||
* @len: length of channel status buffer
|
||||
*
|
||||
* Create the consumer format channel status data in @cs of maximum size
|
||||
* @len corresponding to the parameters of the PCM runtime @runtime.
|
||||
*
|
||||
* Drivers may wish to tweak the contents of the buffer after creation.
|
||||
*
|
||||
* Returns: length of buffer, or negative error code if something failed.
|
||||
*/
|
||||
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
|
||||
u8 *cs, size_t len)
|
||||
{
|
||||
return create_iec958_consumer(params_rate(params), params_width(params),
|
||||
cs, len);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
|
||||
|
|
|
@ -16,6 +16,16 @@ static inline int get_wcaps_type(unsigned int wcaps)
|
|||
return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
|
||||
}
|
||||
|
||||
static inline unsigned int get_wcaps_channels(u32 wcaps)
|
||||
{
|
||||
unsigned int chans;
|
||||
|
||||
chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
|
||||
chans = (chans + 1) * 2;
|
||||
|
||||
return chans;
|
||||
}
|
||||
|
||||
extern const struct attribute_group *hdac_dev_attr_groups[];
|
||||
int hda_widget_sysfs_init(struct hdac_device *codec);
|
||||
void hda_widget_sysfs_exit(struct hdac_device *codec);
|
||||
|
|
|
@ -88,6 +88,7 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_MC13783 if MFD_MC13XXX
|
||||
select SND_SOC_ML26124 if I2C
|
||||
select SND_SOC_NAU8825 if I2C
|
||||
select SND_SOC_HDMI_CODEC
|
||||
select SND_SOC_PCM1681 if I2C
|
||||
select SND_SOC_PCM179X_I2C if I2C
|
||||
select SND_SOC_PCM179X_SPI if SPI_MASTER
|
||||
|
@ -478,6 +479,11 @@ config SND_SOC_BT_SCO
|
|||
config SND_SOC_DMIC
|
||||
tristate
|
||||
|
||||
config SND_SOC_HDMI_CODEC
|
||||
tristate
|
||||
select SND_PCM_ELD
|
||||
select SND_PCM_IEC958
|
||||
|
||||
config SND_SOC_ES8328
|
||||
tristate "Everest Semi ES8328 CODEC"
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ snd-soc-max9850-objs := max9850.o
|
|||
snd-soc-mc13783-objs := mc13783.o
|
||||
snd-soc-ml26124-objs := ml26124.o
|
||||
snd-soc-nau8825-objs := nau8825.o
|
||||
snd-soc-hdmi-codec-objs := hdmi-codec.o
|
||||
snd-soc-pcm1681-objs := pcm1681.o
|
||||
snd-soc-pcm179x-codec-objs := pcm179x.o
|
||||
snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
|
||||
|
@ -291,6 +292,7 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
|
|||
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
|
||||
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
|
||||
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
|
||||
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
|
||||
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
|
||||
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
|
||||
obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include <sound/pcm_drm_eld.h>
|
||||
#include <sound/hda_chmap.h>
|
||||
#include "../../hda/local.h"
|
||||
#include "hdac_hdmi.h"
|
||||
|
||||
|
@ -60,11 +61,17 @@ struct hdac_hdmi_cvt {
|
|||
struct hdac_hdmi_cvt_params params;
|
||||
};
|
||||
|
||||
/* Currently only spk_alloc, more to be added */
|
||||
struct hdac_hdmi_parsed_eld {
|
||||
u8 spk_alloc;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_eld {
|
||||
bool monitor_present;
|
||||
bool eld_valid;
|
||||
int eld_size;
|
||||
char eld_buffer[ELD_MAX_SIZE];
|
||||
struct hdac_hdmi_parsed_eld info;
|
||||
};
|
||||
|
||||
struct hdac_hdmi_pin {
|
||||
|
@ -76,6 +83,10 @@ struct hdac_hdmi_pin {
|
|||
struct hdac_ext_device *edev;
|
||||
int repoll_count;
|
||||
struct delayed_work work;
|
||||
struct mutex lock;
|
||||
bool chmap_set;
|
||||
unsigned char chmap[8]; /* ALSA API channel-map */
|
||||
int channels; /* current number of channels */
|
||||
};
|
||||
|
||||
struct hdac_hdmi_pcm {
|
||||
|
@ -100,8 +111,22 @@ struct hdac_hdmi_priv {
|
|||
int num_pin;
|
||||
int num_cvt;
|
||||
struct mutex pin_mutex;
|
||||
struct hdac_chmap chmap;
|
||||
};
|
||||
|
||||
static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
|
||||
int pcm_idx)
|
||||
{
|
||||
struct hdac_hdmi_pcm *pcm;
|
||||
|
||||
list_for_each_entry(pcm, &hdmi->pcm_list, head) {
|
||||
if (pcm->pcm_id == pcm_idx)
|
||||
return pcm;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
|
||||
{
|
||||
struct hdac_device *hdac = dev_to_hdac_dev(dev);
|
||||
|
@ -278,26 +303,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
|
|||
int i;
|
||||
const u8 *eld_buf;
|
||||
u8 conn_type;
|
||||
int channels = 2;
|
||||
int channels, ca;
|
||||
|
||||
list_for_each_entry(pin, &hdmi->pin_list, head) {
|
||||
if (pin->nid == pin_nid)
|
||||
break;
|
||||
}
|
||||
|
||||
ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc,
|
||||
pin->channels, pin->chmap_set, true, pin->chmap);
|
||||
|
||||
channels = snd_hdac_get_active_channels(ca);
|
||||
hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels);
|
||||
|
||||
snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
|
||||
pin->channels, pin->chmap, pin->chmap_set);
|
||||
|
||||
eld_buf = pin->eld.eld_buffer;
|
||||
conn_type = drm_eld_get_conn_type(eld_buf);
|
||||
|
||||
/* setup channel count */
|
||||
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
|
||||
|
||||
switch (conn_type) {
|
||||
case DRM_ELD_CONN_TYPE_HDMI:
|
||||
hdmi_audio_infoframe_init(&frame);
|
||||
|
||||
/* Default stereo for now */
|
||||
frame.channels = channels;
|
||||
frame.channel_allocation = ca;
|
||||
|
||||
ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
|
||||
if (ret < 0)
|
||||
|
@ -311,7 +341,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
|
|||
dp_ai.len = 0x1b;
|
||||
dp_ai.ver = 0x11 << 2;
|
||||
dp_ai.CC02_CT47 = channels - 1;
|
||||
dp_ai.CA = 0;
|
||||
dp_ai.CA = ca;
|
||||
|
||||
dip = (u8 *)&dp_ai;
|
||||
break;
|
||||
|
@ -370,17 +400,23 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
|
|||
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_hdmi_pin *pin;
|
||||
struct hdac_ext_dma_params *dd;
|
||||
int ret;
|
||||
|
||||
dai_map = &hdmi->dai_map[dai->id];
|
||||
pin = dai_map->pin;
|
||||
|
||||
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);
|
||||
|
||||
mutex_lock(&pin->lock);
|
||||
pin->channels = substream->runtime->channels;
|
||||
|
||||
ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
|
||||
dai_map->pin->nid);
|
||||
mutex_unlock(&pin->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -640,6 +676,12 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
|||
snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
|
||||
|
||||
mutex_lock(&dai_map->pin->lock);
|
||||
dai_map->pin->chmap_set = false;
|
||||
memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap));
|
||||
dai_map->pin->channels = 0;
|
||||
mutex_unlock(&dai_map->pin->lock);
|
||||
|
||||
dai_map->pin = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -647,10 +689,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
|
|||
static int
|
||||
hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
|
||||
{
|
||||
unsigned int chans;
|
||||
struct hdac_ext_device *edev = to_ehdac_device(hdac);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
int err;
|
||||
|
||||
/* Only stereo supported as of now */
|
||||
cvt->params.channels_min = cvt->params.channels_max = 2;
|
||||
chans = get_wcaps(hdac, cvt->nid);
|
||||
chans = get_wcaps_channels(chans);
|
||||
|
||||
cvt->params.channels_min = 2;
|
||||
|
||||
cvt->params.channels_max = chans;
|
||||
if (chans > hdmi->chmap.channels_max)
|
||||
hdmi->chmap.channels_max = chans;
|
||||
|
||||
err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
|
||||
&cvt->params.rates,
|
||||
|
@ -1008,6 +1059,12 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
|
|||
return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
|
||||
}
|
||||
|
||||
static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
|
||||
struct hdac_hdmi_pin *pin)
|
||||
{
|
||||
pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER];
|
||||
}
|
||||
|
||||
static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
|
||||
{
|
||||
struct hdac_ext_device *edev = pin->edev;
|
||||
|
@ -1065,6 +1122,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
|
|||
|
||||
snd_jack_report(pcm->jack, SND_JACK_AVOUT);
|
||||
}
|
||||
hdac_hdmi_parse_eld(edev, pin);
|
||||
|
||||
print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
|
||||
pin->eld.eld_buffer, pin->eld.eld_size);
|
||||
|
@ -1123,6 +1181,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
|
|||
hdmi->num_pin++;
|
||||
|
||||
pin->edev = edev;
|
||||
mutex_init(&pin->lock);
|
||||
INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
|
||||
|
||||
return 0;
|
||||
|
@ -1342,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = {
|
|||
.pin_eld_notify = hdac_hdmi_eld_notify_cb,
|
||||
};
|
||||
|
||||
static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
|
||||
int device)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (rtd->pcm && (rtd->pcm->device == device))
|
||||
return rtd->pcm;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
|
||||
{
|
||||
char jack_name[NAME_SIZE];
|
||||
|
@ -1351,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
|
|||
snd_soc_component_get_dapm(&codec->component);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pcm *pcm;
|
||||
struct snd_pcm *snd_pcm;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* this is a new PCM device, create new pcm and
|
||||
|
@ -1362,6 +1436,18 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
|
|||
pcm->pcm_id = device;
|
||||
pcm->cvt = hdmi->dai_map[dai->id].cvt;
|
||||
|
||||
snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
|
||||
if (snd_pcm) {
|
||||
err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
|
||||
if (err < 0) {
|
||||
dev_err(&edev->hdac.dev,
|
||||
"chmap control add failed with err: %d for pcm: %d\n",
|
||||
err, device);
|
||||
kfree(pcm);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&pcm->head, &hdmi->pcm_list);
|
||||
|
||||
sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
|
||||
|
@ -1483,6 +1569,60 @@ static struct snd_soc_codec_driver hdmi_hda_codec = {
|
|||
.idle_bias_off = true,
|
||||
};
|
||||
|
||||
static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
|
||||
unsigned char *chmap)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_ehdac_device(hdac);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
|
||||
struct hdac_hdmi_pin *pin = pcm->pin;
|
||||
|
||||
/* chmap is already set to 0 in caller */
|
||||
if (!pin)
|
||||
return;
|
||||
|
||||
memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap));
|
||||
}
|
||||
|
||||
static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
|
||||
unsigned char *chmap, int prepared)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_ehdac_device(hdac);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
|
||||
struct hdac_hdmi_pin *pin = pcm->pin;
|
||||
|
||||
mutex_lock(&pin->lock);
|
||||
pin->chmap_set = true;
|
||||
memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap));
|
||||
if (prepared)
|
||||
hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid);
|
||||
mutex_unlock(&pin->lock);
|
||||
}
|
||||
|
||||
static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_ehdac_device(hdac);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
|
||||
struct hdac_hdmi_pin *pin = pcm->pin;
|
||||
|
||||
return pin ? true:false;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
|
||||
{
|
||||
struct hdac_ext_device *edev = to_ehdac_device(hdac);
|
||||
struct hdac_hdmi_priv *hdmi = edev->private_data;
|
||||
struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
|
||||
struct hdac_hdmi_pin *pin = pcm->pin;
|
||||
|
||||
if (!pin || !pin->eld.eld_valid)
|
||||
return 0;
|
||||
|
||||
return pin->eld.info.spk_alloc;
|
||||
}
|
||||
|
||||
static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
|
||||
{
|
||||
struct hdac_device *codec = &edev->hdac;
|
||||
|
@ -1501,6 +1641,11 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
|
|||
return -ENOMEM;
|
||||
|
||||
edev->private_data = hdmi_priv;
|
||||
snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap);
|
||||
hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
|
||||
hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
|
||||
hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
|
||||
hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
|
||||
|
||||
dev_set_drvdata(&codec->dev, edev);
|
||||
|
||||
|
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* ALSA SoC codec for HDMI encoder drivers
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Author: Jyri Sarha <jsarha@ti.com>
|
||||
*
|
||||
* 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/string.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_drm_eld.h>
|
||||
#include <sound/hdmi-codec.h>
|
||||
#include <sound/pcm_iec958.h>
|
||||
|
||||
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
|
||||
|
||||
struct hdmi_codec_priv {
|
||||
struct hdmi_codec_pdata hcd;
|
||||
struct snd_soc_dai_driver *daidrv;
|
||||
struct hdmi_codec_daifmt daifmt[2];
|
||||
struct mutex current_stream_lock;
|
||||
struct snd_pcm_substream *current_stream;
|
||||
struct snd_pcm_hw_constraint_list ratec;
|
||||
uint8_t eld[MAX_ELD_BYTES];
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
|
||||
SND_SOC_DAPM_OUTPUT("TX"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route hdmi_routes[] = {
|
||||
{ "TX", NULL, "Playback" },
|
||||
};
|
||||
|
||||
enum {
|
||||
DAI_ID_I2S = 0,
|
||||
DAI_ID_SPDIF,
|
||||
};
|
||||
|
||||
static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||
uinfo->count = sizeof(hcp->eld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
||||
|
||||
memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_controls[] = {
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "ELD",
|
||||
.info = hdmi_eld_ctl_info,
|
||||
.get = hdmi_eld_ctl_get,
|
||||
},
|
||||
};
|
||||
|
||||
static int hdmi_codec_new_stream(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&hcp->current_stream_lock);
|
||||
if (!hcp->current_stream) {
|
||||
hcp->current_stream = substream;
|
||||
} else if (hcp->current_stream != substream) {
|
||||
dev_err(dai->dev, "Only one simultaneous stream supported!\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&hcp->current_stream_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(dai->dev, "%s()\n", __func__);
|
||||
|
||||
ret = hdmi_codec_new_stream(substream, dai);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (hcp->hcd.ops->audio_startup) {
|
||||
ret = hcp->hcd.ops->audio_startup(dai->dev->parent);
|
||||
if (ret) {
|
||||
mutex_lock(&hcp->current_stream_lock);
|
||||
hcp->current_stream = NULL;
|
||||
mutex_unlock(&hcp->current_stream_lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (hcp->hcd.ops->get_eld) {
|
||||
ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld,
|
||||
sizeof(hcp->eld));
|
||||
|
||||
if (!ret) {
|
||||
ret = snd_pcm_hw_constraint_eld(substream->runtime,
|
||||
hcp->eld);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dai->dev, "%s()\n", __func__);
|
||||
|
||||
WARN_ON(hcp->current_stream != substream);
|
||||
|
||||
hcp->hcd.ops->audio_shutdown(dai->dev->parent);
|
||||
|
||||
mutex_lock(&hcp->current_stream_lock);
|
||||
hcp->current_stream = NULL;
|
||||
mutex_unlock(&hcp->current_stream_lock);
|
||||
}
|
||||
|
||||
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdmi_codec_params hp = {
|
||||
.iec = {
|
||||
.status = { 0 },
|
||||
.subcode = { 0 },
|
||||
.pad = 0,
|
||||
.dig_subframe = { 0 },
|
||||
}
|
||||
};
|
||||
int ret;
|
||||
|
||||
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
|
||||
params_width(params), params_rate(params),
|
||||
params_channels(params));
|
||||
|
||||
if (params_width(params) > 24)
|
||||
params->msbits = 24;
|
||||
|
||||
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
|
||||
sizeof(hp.iec.status));
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hdmi_codec_new_stream(substream, dai);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hdmi_audio_infoframe_init(&hp.cea);
|
||||
hp.cea.channels = params_channels(params);
|
||||
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
|
||||
hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
|
||||
hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
|
||||
|
||||
hp.sample_width = params_width(params);
|
||||
hp.sample_rate = params_rate(params);
|
||||
hp.channels = params_channels(params);
|
||||
|
||||
return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id],
|
||||
&hp);
|
||||
}
|
||||
|
||||
static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdmi_codec_daifmt cf = { 0 };
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(dai->dev, "%s()\n", __func__);
|
||||
|
||||
if (dai->id == DAI_ID_SPDIF) {
|
||||
cf.fmt = HDMI_SPDIF;
|
||||
} else {
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
cf.bit_clk_master = 1;
|
||||
cf.frame_clk_master = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
cf.frame_clk_master = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
cf.bit_clk_master = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
cf.frame_clk_inv = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
cf.bit_clk_inv = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
cf.frame_clk_inv = 1;
|
||||
cf.bit_clk_inv = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
cf.fmt = HDMI_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
cf.fmt = HDMI_DSP_A;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
cf.fmt = HDMI_DSP_B;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
cf.fmt = HDMI_RIGHT_J;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
cf.fmt = HDMI_LEFT_J;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_AC97:
|
||||
cf.fmt = HDMI_AC97;
|
||||
break;
|
||||
default:
|
||||
dev_err(dai->dev, "Invalid DAI interface format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
hcp->daifmt[dai->id] = cf;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(dai->dev, "%s()\n", __func__);
|
||||
|
||||
if (hcp->hcd.ops->digital_mute)
|
||||
return hcp->hcd.ops->digital_mute(dai->dev->parent, mute);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops hdmi_dai_ops = {
|
||||
.startup = hdmi_codec_startup,
|
||||
.shutdown = hdmi_codec_shutdown,
|
||||
.hw_params = hdmi_codec_hw_params,
|
||||
.set_fmt = hdmi_codec_set_fmt,
|
||||
.digital_mute = hdmi_codec_digital_mute,
|
||||
};
|
||||
|
||||
|
||||
#define HDMI_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)
|
||||
|
||||
#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
|
||||
|
||||
/*
|
||||
* This list is only for formats allowed on the I2S bus. So there is
|
||||
* some formats listed that are not supported by HDMI interface. For
|
||||
* instance allowing the 32-bit formats enables 24-precision with CPU
|
||||
* DAIs that do not support 24-bit formats. If the extra formats cause
|
||||
* problems, we should add the video side driver an option to disable
|
||||
* them.
|
||||
*/
|
||||
#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
|
||||
|
||||
static struct snd_soc_dai_driver hdmi_i2s_dai = {
|
||||
.name = "i2s-hifi",
|
||||
.id = DAI_ID_I2S,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.rates = HDMI_RATES,
|
||||
.formats = I2S_FORMATS,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
.ops = &hdmi_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
|
||||
.name = "spdif-hifi",
|
||||
.id = DAI_ID_SPDIF,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = HDMI_RATES,
|
||||
.formats = SPDIF_FORMATS,
|
||||
},
|
||||
.ops = &hdmi_dai_ops,
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_driver hdmi_codec = {
|
||||
.controls = hdmi_controls,
|
||||
.num_controls = ARRAY_SIZE(hdmi_controls),
|
||||
.dapm_widgets = hdmi_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
|
||||
.dapm_routes = hdmi_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
|
||||
};
|
||||
|
||||
static int hdmi_codec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct hdmi_codec_priv *hcp;
|
||||
int dai_count, i = 0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
if (!hcd) {
|
||||
dev_err(dev, "%s: No plalform data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dai_count = hcd->i2s + hcd->spdif;
|
||||
if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
|
||||
!hcd->ops->audio_shutdown) {
|
||||
dev_err(dev, "%s: Invalid parameters\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL);
|
||||
if (!hcp)
|
||||
return -ENOMEM;
|
||||
|
||||
hcp->hcd = *hcd;
|
||||
mutex_init(&hcp->current_stream_lock);
|
||||
|
||||
hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
|
||||
GFP_KERNEL);
|
||||
if (!hcp->daidrv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (hcd->i2s) {
|
||||
hcp->daidrv[i] = hdmi_i2s_dai;
|
||||
hcp->daidrv[i].playback.channels_max =
|
||||
hcd->max_i2s_channels;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (hcd->spdif)
|
||||
hcp->daidrv[i] = hdmi_spdif_dai;
|
||||
|
||||
ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
|
||||
dai_count);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, hcp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver hdmi_codec_driver = {
|
||||
.driver = {
|
||||
.name = HDMI_CODEC_DRV_NAME,
|
||||
},
|
||||
.probe = hdmi_codec_probe,
|
||||
.remove = hdmi_codec_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(hdmi_codec_driver);
|
||||
|
||||
MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
|
||||
MODULE_DESCRIPTION("HDMI Audio Codec Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
|
|
@ -30,6 +30,16 @@
|
|||
static struct snd_soc_jack skylake_headset;
|
||||
static struct snd_soc_card skylake_audio_card;
|
||||
|
||||
struct skl_hdmi_pcm {
|
||||
struct list_head head;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int device;
|
||||
};
|
||||
|
||||
struct skl_nau8825_private {
|
||||
struct list_head hdmi_pcm_list;
|
||||
};
|
||||
|
||||
enum {
|
||||
SKL_DPCM_AUDIO_PB = 0,
|
||||
SKL_DPCM_AUDIO_CP,
|
||||
|
@ -192,23 +202,56 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
|
|||
|
||||
static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
|
||||
pcm->codec_dai = dai;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
|
||||
pcm->codec_dai = dai;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
|
||||
pcm->codec_dai = dai;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
|
@ -533,6 +576,21 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int skylake_card_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(card);
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
|
||||
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* skylake audio machine driver for SPT + NAU88L25 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825max",
|
||||
|
@ -546,11 +604,21 @@ static struct snd_soc_card skylake_audio_card = {
|
|||
.dapm_routes = skylake_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_map),
|
||||
.fully_routed = true,
|
||||
.late_probe = skylake_card_late_probe,
|
||||
};
|
||||
|
||||
static int skylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct skl_nau8825_private *ctx;
|
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
|
||||
|
||||
skylake_audio_card.dev = &pdev->dev;
|
||||
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,15 @@
|
|||
static struct snd_soc_jack skylake_headset;
|
||||
static struct snd_soc_card skylake_audio_card;
|
||||
|
||||
struct skl_hdmi_pcm {
|
||||
struct list_head head;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int device;
|
||||
};
|
||||
|
||||
struct skl_nau88125_private {
|
||||
struct list_head hdmi_pcm_list;
|
||||
};
|
||||
enum {
|
||||
SKL_DPCM_AUDIO_PB = 0,
|
||||
SKL_DPCM_AUDIO_CP,
|
||||
|
@ -222,24 +231,57 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
|
|||
|
||||
static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
|
||||
pcm->codec_dai = dai;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
|
||||
pcm->codec_dai = dai;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
|
||||
pcm->codec_dai = dai;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
|
||||
|
@ -584,6 +626,21 @@ static struct snd_soc_dai_link skylake_dais[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int skylake_card_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(card);
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
|
||||
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* skylake audio machine driver for SPT + NAU88L25 */
|
||||
static struct snd_soc_card skylake_audio_card = {
|
||||
.name = "sklnau8825adi",
|
||||
|
@ -599,11 +656,21 @@ static struct snd_soc_card skylake_audio_card = {
|
|||
.codec_conf = ssm4567_codec_conf,
|
||||
.num_configs = ARRAY_SIZE(ssm4567_codec_conf),
|
||||
.fully_routed = true,
|
||||
.late_probe = skylake_card_late_probe,
|
||||
};
|
||||
|
||||
static int skylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct skl_nau88125_private *ctx;
|
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
|
||||
|
||||
skylake_audio_card.dev = &pdev->dev;
|
||||
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,16 @@
|
|||
|
||||
static struct snd_soc_jack skylake_headset;
|
||||
|
||||
struct skl_hdmi_pcm {
|
||||
struct list_head head;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int device;
|
||||
};
|
||||
|
||||
struct skl_rt286_private {
|
||||
struct list_head hdmi_pcm_list;
|
||||
};
|
||||
|
||||
enum {
|
||||
SKL_DPCM_AUDIO_PB = 0,
|
||||
SKL_DPCM_AUDIO_CP,
|
||||
|
@ -142,9 +152,20 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
|
|||
|
||||
static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai = rtd->codec_dai;
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
|
||||
return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id);
|
||||
pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id;
|
||||
pcm->codec_dai = dai;
|
||||
|
||||
list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {
|
||||
|
@ -437,6 +458,21 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int skylake_card_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(card);
|
||||
struct skl_hdmi_pcm *pcm;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
|
||||
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* skylake audio machine driver for SPT + RT286S */
|
||||
static struct snd_soc_card skylake_rt286 = {
|
||||
.name = "skylake-rt286",
|
||||
|
@ -450,11 +486,21 @@ static struct snd_soc_card skylake_rt286 = {
|
|||
.dapm_routes = skylake_rt286_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
|
||||
.fully_routed = true,
|
||||
.late_probe = skylake_card_late_probe,
|
||||
};
|
||||
|
||||
static int skylake_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct skl_rt286_private *ctx;
|
||||
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
|
||||
|
||||
skylake_rt286.dev = &pdev->dev;
|
||||
snd_soc_card_set_drvdata(&skylake_rt286, ctx);
|
||||
|
||||
return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
|
|||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = HDA_QUAD,
|
||||
.channels_max = 8,
|
||||
.buffer_bytes_max = AZX_MAX_BUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = AZX_MAX_BUF_SIZE / 2,
|
||||
|
@ -691,7 +691,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.playback = {
|
||||
.stream_name = "HDMI1 Playback",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.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 |
|
||||
|
@ -706,7 +706,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.playback = {
|
||||
.stream_name = "HDMI2 Playback",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.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 |
|
||||
|
@ -721,7 +721,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.playback = {
|
||||
.stream_name = "HDMI3 Playback",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.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 |
|
||||
|
@ -846,7 +846,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.playback = {
|
||||
.stream_name = "iDisp1 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE,
|
||||
|
@ -858,7 +858,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.playback = {
|
||||
.stream_name = "iDisp2 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
|
||||
SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
|
||||
|
@ -871,7 +871,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
|
|||
.playback = {
|
||||
.stream_name = "iDisp3 Tx",
|
||||
.channels_min = HDA_STEREO,
|
||||
.channels_max = HDA_STEREO,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
|
||||
SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
|
||||
|
|
|
@ -154,13 +154,32 @@ static void skl_dump_mconfig(struct skl_sst *ctx,
|
|||
dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
|
||||
}
|
||||
|
||||
static void skl_tplg_update_chmap(struct skl_module_fmt *fmt, int chs)
|
||||
{
|
||||
int slot_map = 0xFFFFFFFF;
|
||||
int start_slot = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chs; i++) {
|
||||
/*
|
||||
* For 2 channels with starting slot as 0, slot map will
|
||||
* look like 0xFFFFFF10.
|
||||
*/
|
||||
slot_map &= (~(0xF << (4 * i)) | (start_slot << (4 * i)));
|
||||
start_slot++;
|
||||
}
|
||||
fmt->ch_map = slot_map;
|
||||
}
|
||||
|
||||
static void skl_tplg_update_params(struct skl_module_fmt *fmt,
|
||||
struct skl_pipe_params *params, int fixup)
|
||||
{
|
||||
if (fixup & SKL_RATE_FIXUP_MASK)
|
||||
fmt->s_freq = params->s_freq;
|
||||
if (fixup & SKL_CH_FIXUP_MASK)
|
||||
if (fixup & SKL_CH_FIXUP_MASK) {
|
||||
fmt->channels = params->ch;
|
||||
skl_tplg_update_chmap(fmt, fmt->channels);
|
||||
}
|
||||
if (fixup & SKL_FMT_FIXUP_MASK) {
|
||||
fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
|
||||
|
||||
|
|
Loading…
Reference in New Issue