Merge remote-tracking branches 'asoc/topic/atmel', 'asoc/topic/chmap', 'asoc/topic/cq93vc' and 'asoc/topic/da7218' into asoc-next

This commit is contained in:
Mark Brown 2017-02-19 16:36:13 +00:00
11 changed files with 511 additions and 28 deletions

View File

@ -24,6 +24,8 @@ Optional properties:
this parameter to choose where the clock from. this parameter to choose where the clock from.
- By default the clock is from TK pin, if the clock from RK pin, this - By default the clock is from TK pin, if the clock from RK pin, this
property is needed. property is needed.
- #sound-dai-cells: Should contain <0>.
- This property makes the SSC into an automatically registered DAI.
Examples: Examples:
- PDC transfer: - PDC transfer:

View File

@ -2,8 +2,7 @@ Devicetree bindings for the Axentia TSE-850 audio complex
Required properties: Required properties:
- compatible: "axentia,tse850-pcm5142" - compatible: "axentia,tse850-pcm5142"
- axentia,ssc-controller: The phandle of the atmel SSC controller used as - axentia,cpu-dai: The phandle of the cpu dai.
cpu dai.
- axentia,audio-codec: The phandle of the PCM5142 codec. - axentia,audio-codec: The phandle of the PCM5142 codec.
- axentia,add-gpios: gpio specifier that controls the mixer. - axentia,add-gpios: gpio specifier that controls the mixer.
- axentia,loop1-gpios: gpio specifier that controls loop relays on channel 1. - axentia,loop1-gpios: gpio specifier that controls loop relays on channel 1.
@ -43,6 +42,12 @@ the PCM5142 codec.
Example: Example:
&ssc0 {
#sound-dai-cells = <0>;
status = "okay";
};
&i2c { &i2c {
codec: pcm5142@4c { codec: pcm5142@4c {
compatible = "ti,pcm5142"; compatible = "ti,pcm5142";
@ -77,7 +82,7 @@ Example:
sound { sound {
compatible = "axentia,tse850-pcm5142"; compatible = "axentia,tse850-pcm5142";
axentia,ssc-controller = <&ssc0>; axentia,cpu-dai = <&ssc0>;
axentia,audio-codec = <&codec>; axentia,audio-codec = <&codec>;
axentia,add-gpios = <&pioA 8 GPIO_ACTIVE_LOW>; axentia,add-gpios = <&pioA 8 GPIO_ACTIVE_LOW>;

View File

@ -20,6 +20,8 @@
#include <linux/of.h> #include <linux/of.h>
#include "../../sound/soc/atmel/atmel_ssc_dai.h"
/* Serialize access to ssc_list and user count */ /* Serialize access to ssc_list and user count */
static DEFINE_SPINLOCK(user_lock); static DEFINE_SPINLOCK(user_lock);
static LIST_HEAD(ssc_list); static LIST_HEAD(ssc_list);
@ -145,6 +147,49 @@ static inline const struct atmel_ssc_platform_data * __init
platform_get_device_id(pdev)->driver_data; platform_get_device_id(pdev)->driver_data;
} }
#ifdef CONFIG_SND_ATMEL_SOC_SSC
static int ssc_sound_dai_probe(struct ssc_device *ssc)
{
struct device_node *np = ssc->pdev->dev.of_node;
int ret;
int id;
ssc->sound_dai = false;
if (!of_property_read_bool(np, "#sound-dai-cells"))
return 0;
id = of_alias_get_id(np, "ssc");
if (id < 0)
return id;
ret = atmel_ssc_set_audio(id);
ssc->sound_dai = !ret;
return ret;
}
static void ssc_sound_dai_remove(struct ssc_device *ssc)
{
if (!ssc->sound_dai)
return;
atmel_ssc_put_audio(of_alias_get_id(ssc->pdev->dev.of_node, "ssc"));
}
#else
static inline int ssc_sound_dai_probe(struct ssc_device *ssc)
{
if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells"))
return -ENOTSUPP;
return 0;
}
static inline void ssc_sound_dai_remove(struct ssc_device *ssc)
{
}
#endif
static int ssc_probe(struct platform_device *pdev) static int ssc_probe(struct platform_device *pdev)
{ {
struct resource *regs; struct resource *regs;
@ -204,6 +249,9 @@ static int ssc_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n", dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",
ssc->regs, ssc->irq); ssc->regs, ssc->irq);
if (ssc_sound_dai_probe(ssc))
dev_err(&pdev->dev, "failed to auto-setup ssc for audio\n");
return 0; return 0;
} }
@ -211,6 +259,8 @@ static int ssc_remove(struct platform_device *pdev)
{ {
struct ssc_device *ssc = platform_get_drvdata(pdev); struct ssc_device *ssc = platform_get_drvdata(pdev);
ssc_sound_dai_remove(ssc);
spin_lock(&user_lock); spin_lock(&user_lock);
list_del(&ssc->list); list_del(&ssc->list);
spin_unlock(&user_lock); spin_unlock(&user_lock);

View File

@ -248,6 +248,7 @@ struct detailed_timing {
# define DRM_ELD_AUD_SYNCH_DELAY_MAX 0xfa /* 500 ms */ # define DRM_ELD_AUD_SYNCH_DELAY_MAX 0xfa /* 500 ms */
#define DRM_ELD_SPEAKER 7 #define DRM_ELD_SPEAKER 7
# define DRM_ELD_SPEAKER_MASK 0x7f
# define DRM_ELD_SPEAKER_RLRC (1 << 6) # define DRM_ELD_SPEAKER_RLRC (1 << 6)
# define DRM_ELD_SPEAKER_FLRC (1 << 5) # define DRM_ELD_SPEAKER_FLRC (1 << 5)
# define DRM_ELD_SPEAKER_RC (1 << 4) # define DRM_ELD_SPEAKER_RC (1 << 4)
@ -413,6 +414,18 @@ static inline int drm_eld_size(const uint8_t *eld)
return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
} }
/**
* drm_eld_get_spk_alloc - Get speaker allocation
* @eld: pointer to an ELD memory structure
*
* The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER
* field definitions to identify speakers.
*/
static inline u8 drm_eld_get_spk_alloc(const uint8_t *eld)
{
return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK;
}
/** /**
* drm_eld_get_conn_type - Get device type hdmi/dp connected * drm_eld_get_conn_type - Get device type hdmi/dp connected
* @eld: pointer to an ELD memory structure * @eld: pointer to an ELD memory structure

View File

@ -20,6 +20,7 @@ struct ssc_device {
int user; int user;
int irq; int irq;
bool clk_from_rk_pin; bool clk_from_rk_pin;
bool sound_dai;
}; };
struct ssc_device * __must_check ssc_request(unsigned int ssc_num); struct ssc_device * __must_check ssc_request(unsigned int ssc_num);

View File

@ -256,6 +256,9 @@ struct snd_soc_dai_driver {
int (*resume)(struct snd_soc_dai *dai); int (*resume)(struct snd_soc_dai *dai);
/* compress dai */ /* compress dai */
int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
/* Optional Callback used at pcm creation*/
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai);
/* DAI is also used for the control bus */ /* DAI is also used for the control bus */
bool bus_control; bool bus_control;

View File

@ -51,11 +51,7 @@
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include "atmel_ssc_dai.h"
struct tse850_priv { struct tse850_priv {
int ssc_id;
struct gpio_desc *add; struct gpio_desc *add;
struct gpio_desc *loop1; struct gpio_desc *loop1;
struct gpio_desc *loop2; struct gpio_desc *loop2;
@ -329,23 +325,20 @@ static int tse850_dt_init(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct device_node *codec_np, *cpu_np; struct device_node *codec_np, *cpu_np;
struct snd_soc_card *card = &tse850_card;
struct snd_soc_dai_link *dailink = &tse850_dailink; struct snd_soc_dai_link *dailink = &tse850_dailink;
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
if (!np) { if (!np) {
dev_err(&pdev->dev, "only device tree supported\n"); dev_err(&pdev->dev, "only device tree supported\n");
return -EINVAL; return -EINVAL;
} }
cpu_np = of_parse_phandle(np, "axentia,ssc-controller", 0); cpu_np = of_parse_phandle(np, "axentia,cpu-dai", 0);
if (!cpu_np) { if (!cpu_np) {
dev_err(&pdev->dev, "failed to get dai and pcm info\n"); dev_err(&pdev->dev, "failed to get cpu dai\n");
return -EINVAL; return -EINVAL;
} }
dailink->cpu_of_node = cpu_np; dailink->cpu_of_node = cpu_np;
dailink->platform_of_node = cpu_np; dailink->platform_of_node = cpu_np;
tse850->ssc_id = of_alias_get_id(cpu_np, "ssc");
of_node_put(cpu_np); of_node_put(cpu_np);
codec_np = of_parse_phandle(np, "axentia,audio-codec", 0); codec_np = of_parse_phandle(np, "axentia,audio-codec", 0);
@ -415,23 +408,14 @@ static int tse850_probe(struct platform_device *pdev)
return ret; return ret;
} }
ret = atmel_ssc_set_audio(tse850->ssc_id);
if (ret != 0) {
dev_err(dev,
"failed to set SSC %d for audio\n", tse850->ssc_id);
goto err_disable_ana;
}
ret = snd_soc_register_card(card); ret = snd_soc_register_card(card);
if (ret) { if (ret) {
dev_err(dev, "snd_soc_register_card failed\n"); dev_err(dev, "snd_soc_register_card failed\n");
goto err_put_audio; goto err_disable_ana;
} }
return 0; return 0;
err_put_audio:
atmel_ssc_put_audio(tse850->ssc_id);
err_disable_ana: err_disable_ana:
regulator_disable(tse850->ana); regulator_disable(tse850->ana);
return ret; return ret;
@ -443,7 +427,6 @@ static int tse850_remove(struct platform_device *pdev)
struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card); struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
snd_soc_unregister_card(card); snd_soc_unregister_card(card);
atmel_ssc_put_audio(tse850->ssc_id);
regulator_disable(tse850->ana); regulator_disable(tse850->ana);
return 0; return 0;

View File

@ -45,7 +45,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ALC5623 if I2C select SND_SOC_ALC5623 if I2C
select SND_SOC_ALC5632 if I2C select SND_SOC_ALC5632 if I2C
select SND_SOC_BT_SCO select SND_SOC_BT_SCO
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CQ0093VC
select SND_SOC_CS35L32 if I2C select SND_SOC_CS35L32 if I2C
select SND_SOC_CS35L33 if I2C select SND_SOC_CS35L33 if I2C
select SND_SOC_CS35L34 if I2C select SND_SOC_CS35L34 if I2C

View File

@ -1634,7 +1634,8 @@ static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = {
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* DAI */ /* DAI */
SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7218_DAI_TDM_CTRL,
DA7218_DAI_OE_SHIFT, DA7218_NO_INVERT),
SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
/* Output Mixers */ /* Output Mixers */

View File

@ -18,6 +18,7 @@
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/pcm_drm_eld.h> #include <sound/pcm_drm_eld.h>
#include <sound/hdmi-codec.h> #include <sound/hdmi-codec.h>
#include <sound/pcm_iec958.h> #include <sound/pcm_iec958.h>
@ -31,8 +32,261 @@ struct hdmi_device {
}; };
#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list) #define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list)
LIST_HEAD(hdmi_device_list); LIST_HEAD(hdmi_device_list);
static DEFINE_MUTEX(hdmi_mutex);
#define DAI_NAME_SIZE 16 #define DAI_NAME_SIZE 16
#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1
struct hdmi_codec_channel_map_table {
unsigned char map; /* ALSA API channel map position */
unsigned long spk_mask; /* speaker position bit mask */
};
/*
* CEA speaker placement for HDMI 1.4:
*
* FL FLC FC FRC FR FRW
*
* LFE
*
* RL RLC RC RRC RR
*
* Speaker placement has to be extended to support HDMI 2.0
*/
enum hdmi_codec_cea_spk_placement {
FL = BIT(0), /* Front Left */
FC = BIT(1), /* Front Center */
FR = BIT(2), /* Front Right */
FLC = BIT(3), /* Front Left Center */
FRC = BIT(4), /* Front Right Center */
RL = BIT(5), /* Rear Left */
RC = BIT(6), /* Rear Center */
RR = BIT(7), /* Rear Right */
RLC = BIT(8), /* Rear Left Center */
RRC = BIT(9), /* Rear Right Center */
LFE = BIT(10), /* Low Frequency Effect */
};
/*
* cea Speaker allocation structure
*/
struct hdmi_codec_cea_spk_alloc {
const int ca_id;
unsigned int n_ch;
unsigned long mask;
};
/* Channel maps stereo HDMI */
const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = {
{ .channels = 2,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
{ }
};
/* Channel maps for multi-channel playbacks, up to 8 n_ch */
const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = {
{ .channels = 2, /* CA_ID 0x00 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
{ .channels = 4, /* CA_ID 0x01 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA } },
{ .channels = 4, /* CA_ID 0x02 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC } },
{ .channels = 4, /* CA_ID 0x03 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC } },
{ .channels = 6, /* CA_ID 0x04 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 6, /* CA_ID 0x05 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 6, /* CA_ID 0x06 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 6, /* CA_ID 0x07 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 6, /* CA_ID 0x08 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
{ .channels = 6, /* CA_ID 0x09 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
{ .channels = 6, /* CA_ID 0x0A */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
{ .channels = 6, /* CA_ID 0x0B */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
{ .channels = 8, /* CA_ID 0x0C */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 8, /* CA_ID 0x0D */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 8, /* CA_ID 0x0E */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 8, /* CA_ID 0x0F */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
{ .channels = 8, /* CA_ID 0x10 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
{ .channels = 8, /* CA_ID 0x11 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
{ .channels = 8, /* CA_ID 0x12 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
{ .channels = 8, /* CA_ID 0x13 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
{ .channels = 8, /* CA_ID 0x14 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x15 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x16 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x17 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x18 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x19 */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x1A */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x1B */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x1C */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x1D */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x1E */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ .channels = 8, /* CA_ID 0x1F */
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
{ }
};
/*
* hdmi_codec_channel_alloc: speaker configuration available for CEA
*
* This is an ordered list that must match with hdmi_codec_8ch_chmaps struct
* The preceding ones have better chances to be selected by
* hdmi_codec_get_ch_alloc_table_idx().
*/
static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = {
{ .ca_id = 0x00, .n_ch = 2,
.mask = FL | FR},
/* 2.1 */
{ .ca_id = 0x01, .n_ch = 4,
.mask = FL | FR | LFE},
/* Dolby Surround */
{ .ca_id = 0x02, .n_ch = 4,
.mask = FL | FR | FC },
/* surround51 */
{ .ca_id = 0x0b, .n_ch = 6,
.mask = FL | FR | LFE | FC | RL | RR},
/* surround40 */
{ .ca_id = 0x08, .n_ch = 6,
.mask = FL | FR | RL | RR },
/* surround41 */
{ .ca_id = 0x09, .n_ch = 6,
.mask = FL | FR | LFE | RL | RR },
/* surround50 */
{ .ca_id = 0x0a, .n_ch = 6,
.mask = FL | FR | FC | RL | RR },
/* 6.1 */
{ .ca_id = 0x0f, .n_ch = 8,
.mask = FL | FR | LFE | FC | RL | RR | RC },
/* surround71 */
{ .ca_id = 0x13, .n_ch = 8,
.mask = FL | FR | LFE | FC | RL | RR | RLC | RRC },
/* others */
{ .ca_id = 0x03, .n_ch = 8,
.mask = FL | FR | LFE | FC },
{ .ca_id = 0x04, .n_ch = 8,
.mask = FL | FR | RC},
{ .ca_id = 0x05, .n_ch = 8,
.mask = FL | FR | LFE | RC },
{ .ca_id = 0x06, .n_ch = 8,
.mask = FL | FR | FC | RC },
{ .ca_id = 0x07, .n_ch = 8,
.mask = FL | FR | LFE | FC | RC },
{ .ca_id = 0x0c, .n_ch = 8,
.mask = FL | FR | RC | RL | RR },
{ .ca_id = 0x0d, .n_ch = 8,
.mask = FL | FR | LFE | RL | RR | RC },
{ .ca_id = 0x0e, .n_ch = 8,
.mask = FL | FR | FC | RL | RR | RC },
{ .ca_id = 0x10, .n_ch = 8,
.mask = FL | FR | RL | RR | RLC | RRC },
{ .ca_id = 0x11, .n_ch = 8,
.mask = FL | FR | LFE | RL | RR | RLC | RRC },
{ .ca_id = 0x12, .n_ch = 8,
.mask = FL | FR | FC | RL | RR | RLC | RRC },
{ .ca_id = 0x14, .n_ch = 8,
.mask = FL | FR | FLC | FRC },
{ .ca_id = 0x15, .n_ch = 8,
.mask = FL | FR | LFE | FLC | FRC },
{ .ca_id = 0x16, .n_ch = 8,
.mask = FL | FR | FC | FLC | FRC },
{ .ca_id = 0x17, .n_ch = 8,
.mask = FL | FR | LFE | FC | FLC | FRC },
{ .ca_id = 0x18, .n_ch = 8,
.mask = FL | FR | RC | FLC | FRC },
{ .ca_id = 0x19, .n_ch = 8,
.mask = FL | FR | LFE | RC | FLC | FRC },
{ .ca_id = 0x1a, .n_ch = 8,
.mask = FL | FR | RC | FC | FLC | FRC },
{ .ca_id = 0x1b, .n_ch = 8,
.mask = FL | FR | LFE | RC | FC | FLC | FRC },
{ .ca_id = 0x1c, .n_ch = 8,
.mask = FL | FR | RL | RR | FLC | FRC },
{ .ca_id = 0x1d, .n_ch = 8,
.mask = FL | FR | LFE | RL | RR | FLC | FRC },
{ .ca_id = 0x1e, .n_ch = 8,
.mask = FL | FR | FC | RL | RR | FLC | FRC },
{ .ca_id = 0x1f, .n_ch = 8,
.mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
};
struct hdmi_codec_priv { struct hdmi_codec_priv {
struct hdmi_codec_pdata hcd; struct hdmi_codec_pdata hcd;
struct snd_soc_dai_driver *daidrv; struct snd_soc_dai_driver *daidrv;
@ -41,6 +295,8 @@ struct hdmi_codec_priv {
struct snd_pcm_substream *current_stream; struct snd_pcm_substream *current_stream;
struct snd_pcm_hw_constraint_list ratec; struct snd_pcm_hw_constraint_list ratec;
uint8_t eld[MAX_ELD_BYTES]; uint8_t eld[MAX_ELD_BYTES];
struct snd_pcm_chmap *chmap_info;
unsigned int chmap_idx;
}; };
static const struct snd_soc_dapm_widget hdmi_widgets[] = { static const struct snd_soc_dapm_widget hdmi_widgets[] = {
@ -79,6 +335,83 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc)
{
int i;
const unsigned long hdmi_codec_eld_spk_alloc_bits[] = {
[0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR,
[4] = RC, [5] = FLC | FRC, [6] = RLC | RRC,
};
unsigned long spk_mask = 0;
for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) {
if (spk_alloc & (1 << i))
spk_mask |= hdmi_codec_eld_spk_alloc_bits[i];
}
return spk_mask;
}
void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp)
{
u8 spk_alloc;
unsigned long spk_mask;
spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
/* Detect if only stereo supported, else return 8 channels mappings */
if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2)
hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps;
else
hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
}
static int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp,
unsigned char channels)
{
int i;
u8 spk_alloc;
unsigned long spk_mask;
const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc;
spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) {
/* If spk_alloc == 0, HDMI is unplugged return stereo config*/
if (!spk_alloc && cap->ca_id == 0)
return i;
if (cap->n_ch != channels)
continue;
if (!(cap->mask == (spk_mask & cap->mask)))
continue;
return i;
}
return -EINVAL;
}
static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
unsigned const char *map;
unsigned int i;
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = info->private_data;
map = info->chmap[hcp->chmap_idx].map;
for (i = 0; i < info->max_channels; i++) {
if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN)
ucontrol->value.integer.value[i] = 0;
else
ucontrol->value.integer.value[i] = map[i];
}
return 0;
}
static const struct snd_kcontrol_new hdmi_controls[] = { static const struct snd_kcontrol_new hdmi_controls[] = {
{ {
.access = SNDRV_CTL_ELEM_ACCESS_READ | .access = SNDRV_CTL_ELEM_ACCESS_READ |
@ -140,6 +473,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
if (ret) if (ret)
return ret; return ret;
} }
/* Select chmap supported */
hdmi_codec_eld_chmap(hcp);
} }
return 0; return 0;
} }
@ -153,6 +488,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
WARN_ON(hcp->current_stream != substream); WARN_ON(hcp->current_stream != substream);
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
mutex_lock(&hcp->current_stream_lock); mutex_lock(&hcp->current_stream_lock);
@ -173,7 +509,7 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
.dig_subframe = { 0 }, .dig_subframe = { 0 },
} }
}; };
int ret; int ret, idx;
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
params_width(params), params_rate(params), params_width(params), params_rate(params),
@ -200,6 +536,17 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
/* Select a channel allocation that matches with ELD and pcm channels */
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
if (idx < 0) {
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
idx);
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
return idx;
}
hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
hp.sample_width = params_width(params); hp.sample_width = params_width(params);
hp.sample_rate = params_rate(params); hp.sample_rate = params_rate(params);
hp.channels = params_channels(params); hp.channels = params_channels(params);
@ -328,6 +675,32 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = {
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai)
{
struct snd_soc_dai_driver *drv = dai->driver;
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
int ret;
dev_dbg(dai->dev, "%s()\n", __func__);
ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
NULL, drv->playback.channels_max, 0,
&hcp->chmap_info);
if (ret < 0)
return ret;
/* override handlers */
hcp->chmap_info->private_data = hcp;
hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get;
/* default chmap supported is stereo */
hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
return 0;
}
static struct snd_soc_dai_driver hdmi_i2s_dai = { static struct snd_soc_dai_driver hdmi_i2s_dai = {
.id = DAI_ID_I2S, .id = DAI_ID_I2S,
.playback = { .playback = {
@ -339,6 +712,7 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = {
.sig_bits = 24, .sig_bits = 24,
}, },
.ops = &hdmi_dai_ops, .ops = &hdmi_dai_ops,
.pcm_new = hdmi_codec_pcm_new,
}; };
static const struct snd_soc_dai_driver hdmi_spdif_dai = { static const struct snd_soc_dai_driver hdmi_spdif_dai = {
@ -351,6 +725,7 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.formats = SPDIF_FORMATS, .formats = SPDIF_FORMATS,
}, },
.ops = &hdmi_dai_ops, .ops = &hdmi_dai_ops,
.pcm_new = hdmi_codec_pcm_new,
}; };
static char hdmi_dai_name[][DAI_NAME_SIZE] = { static char hdmi_dai_name[][DAI_NAME_SIZE] = {
@ -420,6 +795,7 @@ static int hdmi_codec_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
hd = NULL; hd = NULL;
mutex_lock(&hdmi_mutex);
list_for_each(pos, &hdmi_device_list) { list_for_each(pos, &hdmi_device_list) {
struct hdmi_device *tmp = pos_to_hdmi_device(pos); struct hdmi_device *tmp = pos_to_hdmi_device(pos);
@ -431,13 +807,16 @@ static int hdmi_codec_probe(struct platform_device *pdev)
if (!hd) { if (!hd) {
hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL); hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL);
if (!hd) if (!hd) {
mutex_unlock(&hdmi_mutex);
return -ENOMEM; return -ENOMEM;
}
hd->dev = dev->parent; hd->dev = dev->parent;
list_add_tail(&hd->list, &hdmi_device_list); list_add_tail(&hd->list, &hdmi_device_list);
} }
mutex_unlock(&hdmi_mutex);
if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) { if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {
dev_err(dev, "too many hdmi codec are deteced\n"); dev_err(dev, "too many hdmi codec are deteced\n");
@ -479,7 +858,25 @@ static int hdmi_codec_probe(struct platform_device *pdev)
static int hdmi_codec_remove(struct platform_device *pdev) static int hdmi_codec_remove(struct platform_device *pdev)
{ {
snd_soc_unregister_codec(&pdev->dev); struct device *dev = &pdev->dev;
struct list_head *pos;
struct hdmi_codec_priv *hcp;
mutex_lock(&hdmi_mutex);
list_for_each(pos, &hdmi_device_list) {
struct hdmi_device *tmp = pos_to_hdmi_device(pos);
if (tmp->dev == dev->parent) {
list_del(pos);
break;
}
}
mutex_unlock(&hdmi_mutex);
hcp = dev_get_drvdata(dev);
kfree(hcp->chmap_info);
snd_soc_unregister_codec(dev);
return 0; return 0;
} }

View File

@ -1594,6 +1594,27 @@ static int soc_probe_dai(struct snd_soc_dai *dai, int order)
return 0; return 0;
} }
static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais,
struct snd_soc_pcm_runtime *rtd)
{
int i, ret = 0;
for (i = 0; i < num_dais; ++i) {
struct snd_soc_dai_driver *drv = dais[i]->driver;
if (!rtd->dai_link->no_pcm && drv->pcm_new)
ret = drv->pcm_new(rtd, dais[i]);
if (ret < 0) {
dev_err(dais[i]->dev,
"ASoC: Failed to bind %s with pcm device\n",
dais[i]->name);
return ret;
}
}
return 0;
}
static int soc_link_dai_widgets(struct snd_soc_card *card, static int soc_link_dai_widgets(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link, struct snd_soc_dai_link *dai_link,
struct snd_soc_pcm_runtime *rtd) struct snd_soc_pcm_runtime *rtd)
@ -1705,6 +1726,13 @@ static int soc_probe_link_dais(struct snd_soc_card *card,
dai_link->stream_name, ret); dai_link->stream_name, ret);
return ret; return ret;
} }
ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd);
if (ret < 0)
return ret;
ret = soc_link_dai_pcm_new(rtd->codec_dais,
rtd->num_codecs, rtd);
if (ret < 0)
return ret;
} else { } else {
INIT_DELAYED_WORK(&rtd->delayed_work, INIT_DELAYED_WORK(&rtd->delayed_work,
codec2codec_close_delayed_work); codec2codec_close_delayed_work);