From 3bdff244a2bcbde37ec33bf3cdde4638049c6c38 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 15 Jan 2016 16:13:10 +0800 Subject: ALSA: pcm: Add snd_pcm_rate_range_to_bits() This helper function can convert a given sample rate range to SNDRV_PCM_RATE_xxx bits. Signed-off-by: Mengdong Lin Acked-by: Takashi Iwai Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/pcm.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index b0be09279943..af1fb37c6b26 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1093,6 +1093,8 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, unsigned int rates_b); +unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min, + unsigned int rate_max); /** * snd_pcm_set_runtime_buffer - Set the PCM runtime buffer -- cgit v1.2.3-71-gd317 From 64527e8a352968bda529f01df1c9dd5fe581ff04 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 15 Jan 2016 16:13:28 +0800 Subject: ASoC: topology: Add FE DAIs dynamically Topology will create FE DAIs dynamically from the PCM objects, and register them to the component. A PCM topoplogy object describes a FE DAI and DAI link. Later patch will add FE DAI links as well. Change tplg load ops for DAI: - Only process a DAI. - Pass the DAI driver pointer to the component driver for extra initialization. Signed-off-by: Mengdong Lin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-topology.h | 15 ++--- include/sound/soc.h | 2 +- sound/soc/soc-topology.c | 129 +++++++++++++++++++++++++++++-------------- 3 files changed, 92 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index 5b68e3f5aa85..78813adca2a1 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -56,12 +56,6 @@ struct snd_soc_dobj_widget { unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */ }; -/* dynamic PCM DAI object */ -struct snd_soc_dobj_pcm_dai { - struct snd_soc_tplg_pcm_dai *pd; - unsigned int count; -}; - /* generic dynamic object - all dynamic objects belong to this struct */ struct snd_soc_dobj { enum snd_soc_dobj_type type; @@ -71,7 +65,6 @@ struct snd_soc_dobj { union { struct snd_soc_dobj_control control; struct snd_soc_dobj_widget widget; - struct snd_soc_dobj_pcm_dai pcm_dai; }; void *private; /* core does not touch this */ }; @@ -126,10 +119,10 @@ struct snd_soc_tplg_ops { int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); - /* FE - used for any driver specific init */ - int (*pcm_dai_load)(struct snd_soc_component *, - struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe); - int (*pcm_dai_unload)(struct snd_soc_component *, + /* FE DAI - used for any driver specific init */ + int (*dai_load)(struct snd_soc_component *, + struct snd_soc_dai_driver *dai_drv); + int (*dai_unload)(struct snd_soc_component *, struct snd_soc_dobj *); /* callback to handle vendor bespoke data */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 7afb72ceac56..02b4a215fd75 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -27,7 +27,6 @@ #include #include #include -#include /* * Convenience kcontrol builders @@ -404,6 +403,7 @@ struct snd_soc_jack_zone; struct snd_soc_jack_pin; #include #include +#include struct snd_soc_jack_gpio; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 6963ba20991c..446ac9a93aef 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -330,12 +330,12 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, return 0; } -/* pass dynamic FEs configurations to component driver */ -static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg, - struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai) +/* pass DAI configurations to component driver for extra intialization */ +static int soc_tplg_dai_load(struct soc_tplg *tplg, + struct snd_soc_dai_driver *dai_drv) { - if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load) - return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai); + if (tplg->comp && tplg->ops && tplg->ops->dai_load) + return tplg->ops->dai_load(tplg->comp, dai_drv); return 0; } @@ -495,18 +495,21 @@ static void remove_widget(struct snd_soc_component *comp, /* widget w is freed by soc-dapm.c */ } -/* remove PCM DAI configurations */ -static void remove_pcm_dai(struct snd_soc_component *comp, +/* remove DAI configurations */ +static void remove_dai(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, int pass) { + struct snd_soc_dai_driver *dai_drv = + container_of(dobj, struct snd_soc_dai_driver, dobj); + if (pass != SOC_TPLG_PASS_PCM_DAI) return; - if (dobj->ops && dobj->ops->pcm_dai_unload) - dobj->ops->pcm_dai_unload(comp, dobj); + if (dobj->ops && dobj->ops->dai_unload) + dobj->ops->dai_unload(comp, dobj); list_del(&dobj->list); - kfree(dobj); + kfree(dai_drv); } /* bind a kcontrol to it's IO handlers */ @@ -1544,18 +1547,79 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) return 0; } -static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, +static int soc_tplg_dai_create(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *pcm) +{ + struct snd_soc_dai_driver *dai_drv; + struct snd_soc_pcm_stream *stream; + struct snd_soc_tplg_stream_caps *caps; + int ret; + + dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); + if (dai_drv == NULL) + return -ENOMEM; + + dai_drv->name = pcm->dai_name; + dai_drv->id = pcm->dai_id; + + if (pcm->playback) { + stream = &dai_drv->playback; + caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; + + stream->stream_name = kstrdup(caps->name, GFP_KERNEL); + stream->channels_min = caps->channels_min; + stream->channels_max = caps->channels_max; + stream->rates = snd_pcm_rate_range_to_bits(caps->rate_min, + caps->rate_max); + stream->formats = caps->formats; + } + + if (pcm->capture) { + stream = &dai_drv->capture; + caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; + + stream->stream_name = kstrdup(caps->name, GFP_KERNEL); + stream->channels_min = caps->channels_min; + stream->channels_max = caps->channels_max; + stream->rates = snd_pcm_rate_range_to_bits(caps->rate_min, + caps->rate_max); + stream->formats = caps->formats; + } + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_load(tplg, dai_drv); + if (ret < 0) { + dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); + kfree(dai_drv); + return ret; + } + + dai_drv->dobj.index = tplg->index; + dai_drv->dobj.ops = tplg->ops; + dai_drv->dobj.type = SND_SOC_DOBJ_PCM; + list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); + + /* register the DAI to the component */ + return snd_soc_register_dai(tplg->comp, dai_drv); +} + +static int soc_tplg_pcm_create(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *pcm) +{ + return soc_tplg_dai_create(tplg, pcm); +} + +static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { - struct snd_soc_tplg_pcm_dai *pcm_dai; - struct snd_soc_dobj *dobj; + struct snd_soc_tplg_pcm *pcm; int count = hdr->count; - int ret; + int i; if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) return 0; - pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; + pcm = (struct snd_soc_tplg_pcm *)tplg->pos; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_pcm), count, @@ -1565,31 +1629,16 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, return -EINVAL; } + /* create the FE DAIs and DAI links */ + for (i = 0; i < count; i++) { + soc_tplg_pcm_create(tplg, pcm); + pcm++; + } + dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count; - dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); - if (dobj == NULL) - return -ENOMEM; - - /* Call the platform driver call back to register the dais */ - ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count); - if (ret < 0) { - dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n"); - goto err; - } - - dobj->type = get_dobj_type(hdr, NULL); - dobj->pcm_dai.count = count; - dobj->pcm_dai.pd = pcm_dai; - dobj->ops = tplg->ops; - dobj->index = tplg->index; - list_add(&dobj->list, &tplg->comp->dobj_list); return 0; - -err: - kfree(dobj); - return ret; } static int soc_tplg_manifest_load(struct soc_tplg *tplg, @@ -1681,9 +1730,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, case SND_SOC_TPLG_TYPE_DAPM_WIDGET: return soc_tplg_dapm_widget_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_PCM: - case SND_SOC_TPLG_TYPE_DAI_LINK: - case SND_SOC_TPLG_TYPE_CODEC_LINK: - return soc_tplg_pcm_dai_elems_load(tplg, hdr); + return soc_tplg_pcm_elems_load(tplg, hdr); case SND_SOC_TPLG_TYPE_MANIFEST: return soc_tplg_manifest_load(tplg, hdr); default: @@ -1841,9 +1888,7 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) remove_widget(comp, dobj, pass); break; case SND_SOC_DOBJ_PCM: - case SND_SOC_DOBJ_DAI_LINK: - case SND_SOC_DOBJ_CODEC_LINK: - remove_pcm_dai(comp, dobj, pass); + remove_dai(comp, dobj, pass); break; default: dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", -- cgit v1.2.3-71-gd317 From acfc7d46cddcf71cf18772bbe8717b84eac5f672 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 15 Jan 2016 16:13:37 +0800 Subject: ASoC: topology: Add FE DAI links dynamically Topology will also create FE DAI links dynamically from the PCM objects. These links will be removed when the component is removed and its topology info is unloaded. The component driver can implement link_load/unload ops for extra intialization (e.g. error check) and destruction. Signed-off-by: Mengdong Lin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-topology.h | 6 ++++ sound/soc/soc-topology.c | 70 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index 78813adca2a1..b897b9d63161 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -125,6 +125,12 @@ struct snd_soc_tplg_ops { int (*dai_unload)(struct snd_soc_component *, struct snd_soc_dobj *); + /* DAI link - used for any driver specific init */ + int (*link_load)(struct snd_soc_component *, + struct snd_soc_dai_link *link); + int (*link_unload)(struct snd_soc_component *, + struct snd_soc_dobj *); + /* callback to handle vendor bespoke data */ int (*vendor_load)(struct snd_soc_component *, struct snd_soc_tplg_hdr *); diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 446ac9a93aef..0eb01e8155f1 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -340,6 +340,16 @@ static int soc_tplg_dai_load(struct soc_tplg *tplg, return 0; } +/* pass link configurations to component driver for extra intialization */ +static int soc_tplg_dai_link_load(struct soc_tplg *tplg, + struct snd_soc_dai_link *link) +{ + if (tplg->comp && tplg->ops && tplg->ops->link_load) + return tplg->ops->link_load(tplg->comp, link); + + return 0; +} + /* tell the component driver that all firmware has been loaded in this request */ static void soc_tplg_complete(struct soc_tplg *tplg) { @@ -512,6 +522,24 @@ static void remove_dai(struct snd_soc_component *comp, kfree(dai_drv); } +/* remove link configurations */ +static void remove_link(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + struct snd_soc_dai_link *link = + container_of(dobj, struct snd_soc_dai_link, dobj); + + if (pass != SOC_TPLG_PASS_PCM_DAI) + return; + + if (dobj->ops && dobj->ops->link_unload) + dobj->ops->link_unload(comp, dobj); + + list_del(&dobj->list); + snd_soc_remove_dai_link(comp->card, link); + kfree(link); +} + /* bind a kcontrol to it's IO handlers */ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, struct snd_kcontrol_new *k, @@ -1603,10 +1631,47 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, return snd_soc_register_dai(tplg->comp, dai_drv); } +static int soc_tplg_link_create(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm *pcm) +{ + struct snd_soc_dai_link *link; + int ret; + + link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL); + if (link == NULL) + return -ENOMEM; + + link->name = pcm->pcm_name; + link->stream_name = pcm->pcm_name; + + /* pass control to component driver for optional further init */ + ret = soc_tplg_dai_link_load(tplg, link); + if (ret < 0) { + dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); + kfree(link); + return ret; + } + + link->dobj.index = tplg->index; + link->dobj.ops = tplg->ops; + link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + list_add(&link->dobj.list, &tplg->comp->dobj_list); + + snd_soc_add_dai_link(tplg->comp->card, link); + return 0; +} + +/* create a FE DAI and DAI link from the PCM object */ static int soc_tplg_pcm_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { - return soc_tplg_dai_create(tplg, pcm); + int ret; + + ret = soc_tplg_dai_create(tplg, pcm); + if (ret < 0) + return ret; + + return soc_tplg_link_create(tplg, pcm); } static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, @@ -1890,6 +1955,9 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) case SND_SOC_DOBJ_PCM: remove_dai(comp, dobj, pass); break; + case SND_SOC_DOBJ_DAI_LINK: + remove_link(comp, dobj, pass); + break; default: dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", dobj->type); -- cgit v1.2.3-71-gd317 From 1aa8ec255370d3e6f60dfa4a8cdd5e1357b1ab78 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 12 Feb 2016 07:46:08 +0530 Subject: drm/edid: Add API to help find connection type To fill the audio infoframe it is required to identify the connection type as DP or HDMI. This patch adds an API which parses ELD and returns the display type connected. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Reviewed-by: Jani Nikula Cc: David Airlie Cc: dri-devel@lists.freedesktop.org Cc: Daniel Vetter Signed-off-by: Mark Brown --- include/drm/drm_edid.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 2af97691e878..dec6221e8198 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -403,6 +403,18 @@ static inline int drm_eld_size(const uint8_t *eld) return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; } +/** + * drm_eld_get_conn_type - Get device type hdmi/dp connected + * @eld: pointer to an ELD memory structure + * + * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to + * identify the display type connected. + */ +static inline u8 drm_eld_get_conn_type(const uint8_t *eld) +{ + return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK; +} + struct edid *drm_do_get_edid(struct drm_connector *connector, int (*get_edid_block)(void *data, u8 *buf, unsigned int block, size_t len), -- cgit v1.2.3-71-gd317 From fe0d128c57bf927a713159f60a18d9f315d4d91d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 17 Feb 2016 09:44:25 +0100 Subject: ALSA: jack: Allow building the jack layer without input device Since the recent integration of kctl jack and input jack layers, we can basically build the jack layer even without input devices. That is, the jack layer itself can be built with conditional to enable the input device support or not, while the users may enable always CONFIG_SND_JACK unconditionally. For achieving it, this patch changes the following: - A new Kconfig, CONFIG_SND_JACK_INPUT_DEV, was introduced to indicate whether the jack layer supports the input device, - A few items in snd_jack struct and relevant codes are conditionally built upon CONFIG_SND_JACK_INPUT_DEV, - The users of CONFIG_SND_JACK drop the messy dependency on CONFIG_INPUT. This change also automagically fixes a potential bug in HD-audio driver Arnd reported, where the NULL or uninitialized jack instance is dereferenced. Reported-by: Arnd Bergmann Acked-by: Arnd Bergmann Signed-off-by: Takashi Iwai --- include/sound/jack.h | 23 ++++++++++++++--------- sound/core/Kconfig | 9 ++++++--- sound/core/jack.c | 23 ++++++++++++++++++++--- sound/pci/Kconfig | 2 +- sound/pci/hda/Kconfig | 2 +- sound/soc/Kconfig | 2 +- 6 files changed, 43 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/jack.h b/include/sound/jack.h index 23bede121c78..1e84bfb553cf 100644 --- a/include/sound/jack.h +++ b/include/sound/jack.h @@ -72,14 +72,16 @@ enum snd_jack_types { #define SND_JACK_SWITCH_TYPES 6 struct snd_jack { - struct input_dev *input_dev; struct list_head kctl_list; struct snd_card *card; + const char *id; +#ifdef CONFIG_SND_JACK_INPUT_DEV + struct input_dev *input_dev; int registered; int type; - const char *id; char name[100]; unsigned int key[6]; /* Keep in sync with definitions above */ +#endif /* CONFIG_SND_JACK_INPUT_DEV */ void *private_data; void (*private_free)(struct snd_jack *); }; @@ -89,10 +91,11 @@ struct snd_jack { int snd_jack_new(struct snd_card *card, const char *id, int type, struct snd_jack **jack, bool initial_kctl, bool phantom_jack); int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask); +#ifdef CONFIG_SND_JACK_INPUT_DEV void snd_jack_set_parent(struct snd_jack *jack, struct device *parent); int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, int keytype); - +#endif void snd_jack_report(struct snd_jack *jack, int status); #else @@ -107,6 +110,13 @@ static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name return 0; } +static inline void snd_jack_report(struct snd_jack *jack, int status) +{ +} + +#endif + +#if !defined(CONFIG_SND_JACK) || !defined(CONFIG_SND_JACK_INPUT_DEV) static inline void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) { @@ -118,11 +128,6 @@ static inline int snd_jack_set_key(struct snd_jack *jack, { return 0; } - -static inline void snd_jack_report(struct snd_jack *jack, int status) -{ -} - -#endif +#endif /* !CONFIG_SND_JACK || !CONFIG_SND_JACK_INPUT_DEV */ #endif diff --git a/sound/core/Kconfig b/sound/core/Kconfig index a2a1e24becc6..6d12ca9bcb80 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -24,12 +24,15 @@ config SND_RAWMIDI config SND_COMPRESS_OFFLOAD tristate -# To be effective this also requires INPUT - users should say: -# select SND_JACK if INPUT=y || INPUT=SND -# to avoid having to force INPUT on. config SND_JACK bool +# enable input device support in jack layer +config SND_JACK_INPUT_DEV + bool + depends on SND_JACK + default y if INPUT=y || INPUT=SND + config SND_SEQUENCER tristate "Sequencer support" select SND_TIMER diff --git a/sound/core/jack.c b/sound/core/jack.c index 7237acbdcbbc..f652e90efd7e 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -32,6 +32,7 @@ struct snd_jack_kctl { unsigned int mask_bits; /* only masked status bits are reported via kctl */ }; +#ifdef CONFIG_SND_JACK_INPUT_DEV static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_HEADPHONE_INSERT, SW_MICROPHONE_INSERT, @@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_VIDEOOUT_INSERT, SW_LINEIN_INSERT, }; +#endif /* CONFIG_SND_JACK_INPUT_DEV */ static int snd_jack_dev_disconnect(struct snd_device *device) { +#ifdef CONFIG_SND_JACK_INPUT_DEV struct snd_jack *jack = device->device_data; if (!jack->input_dev) @@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device) else input_free_device(jack->input_dev); jack->input_dev = NULL; +#endif /* CONFIG_SND_JACK_INPUT_DEV */ return 0; } @@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device) return 0; } +#ifdef CONFIG_SND_JACK_INPUT_DEV static int snd_jack_dev_register(struct snd_device *device) { struct snd_jack *jack = device->device_data; @@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device) return err; } +#endif /* CONFIG_SND_JACK_INPUT_DEV */ static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl) { @@ -209,11 +215,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, struct snd_jack *jack; struct snd_jack_kctl *jack_kctl = NULL; int err; - int i; static struct snd_device_ops ops = { .dev_free = snd_jack_dev_free, +#ifdef CONFIG_SND_JACK_INPUT_DEV .dev_register = snd_jack_dev_register, .dev_disconnect = snd_jack_dev_disconnect, +#endif /* CONFIG_SND_JACK_INPUT_DEV */ }; if (initial_kctl) { @@ -230,6 +237,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, /* don't creat input device for phantom jack */ if (!phantom_jack) { +#ifdef CONFIG_SND_JACK_INPUT_DEV + int i; + jack->input_dev = input_allocate_device(); if (jack->input_dev == NULL) { err = -ENOMEM; @@ -245,6 +255,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, input_set_capability(jack->input_dev, EV_SW, jack_switch_types[i]); +#endif /* CONFIG_SND_JACK_INPUT_DEV */ } err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); @@ -262,13 +273,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, return 0; fail_input: +#ifdef CONFIG_SND_JACK_INPUT_DEV input_free_device(jack->input_dev); +#endif kfree(jack->id); kfree(jack); return err; } EXPORT_SYMBOL(snd_jack_new); +#ifdef CONFIG_SND_JACK_INPUT_DEV /** * snd_jack_set_parent - Set the parent device for a jack * @@ -326,10 +340,10 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, jack->type |= type; jack->key[key] = keytype; - return 0; } EXPORT_SYMBOL(snd_jack_set_key); +#endif /* CONFIG_SND_JACK_INPUT_DEV */ /** * snd_jack_report - Report the current status of a jack @@ -340,7 +354,9 @@ EXPORT_SYMBOL(snd_jack_set_key); void snd_jack_report(struct snd_jack *jack, int status) { struct snd_jack_kctl *jack_kctl; +#ifdef CONFIG_SND_JACK_INPUT_DEV int i; +#endif if (!jack) return; @@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status) snd_kctl_jack_report(jack->card, jack_kctl->kctl, status & jack_kctl->mask_bits); +#ifdef CONFIG_SND_JACK_INPUT_DEV if (!jack->input_dev) return; @@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status) } input_sync(jack->input_dev); - +#endif /* CONFIG_SND_JACK_INPUT_DEV */ } EXPORT_SYMBOL(snd_jack_report); diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 8f6594a7d37f..32151d8c6bb8 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -866,7 +866,7 @@ config SND_VIRTUOSO select SND_OXYGEN_LIB select SND_PCM select SND_MPU401_UART - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK help Say Y here to include support for sound cards based on the Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX, diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index e94cfd5c69f7..bb02c2d48fd5 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -4,7 +4,7 @@ config SND_HDA tristate select SND_PCM select SND_VMASTER - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK select SND_HDA_CORE config SND_HDA_INTEL diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7ea66ee3653f..182d92efc7c8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -6,7 +6,7 @@ menuconfig SND_SOC tristate "ALSA for SoC audio support" select SND_PCM select AC97_BUS if SND_SOC_AC97_BUS - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER ---help--- -- cgit v1.2.3-71-gd317 From d6dde63e90cf0d34ae61e885a5eb254c59eb3358 Mon Sep 17 00:00:00 2001 From: Andreas Irestål Date: Tue, 16 Feb 2016 13:56:42 +0100 Subject: ASoC: adau17x1: Correct typos in file headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Irestål Signed-off-by: Mark Brown --- include/linux/platform_data/adau17x1.h | 2 +- sound/soc/codecs/adau1761-i2c.c | 2 +- sound/soc/codecs/adau1761-spi.c | 2 +- sound/soc/codecs/adau1761.c | 2 +- sound/soc/codecs/adau1781.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/adau17x1.h b/include/linux/platform_data/adau17x1.h index a81766cae230..9db1b905df24 100644 --- a/include/linux/platform_data/adau17x1.h +++ b/include/linux/platform_data/adau17x1.h @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781 codecs * * Copyright 2011-2014 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index 348ccb17d3cc..a1b12671c307 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2014 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c index 8bc1fbd25fcc..188334928c23 100644 --- a/sound/soc/codecs/adau1761-spi.c +++ b/sound/soc/codecs/adau1761-spi.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2014 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index e7136b1956a3..b95d29dbd13d 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2011-2013 Analog Devices Inc. * Author: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index fde9068550a6..bc1bb56dae63 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1781/ADAU1781 codec + * Driver for ADAU1381/ADAU1781 codec * * Copyright 2011-2013 Analog Devices Inc. * Author: Lars-Peter Clausen -- cgit v1.2.3-71-gd317 From eedf5e72c42b5b155ac52324db72161c958b3af8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Feb 2016 18:09:37 +0100 Subject: ALSA: seq: Remove unimplemented ioctls SNDRV_SEQ_IOCTL_{GET|SET}_QUEUE_OWNER and *_{GET|SET}_QUEUE_SYNC ioctls have been never implemented. Get rid of the definitions from uapi header file. Signed-off-by: Takashi Iwai --- include/uapi/sound/asequencer.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index 5a5fa4956ebd..af96f2044f91 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -594,14 +594,8 @@ struct snd_seq_query_subs { #define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct snd_seq_queue_status) #define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO _IOWR('S', 0x41, struct snd_seq_queue_tempo) #define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO _IOW ('S', 0x42, struct snd_seq_queue_tempo) -#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER _IOWR('S', 0x43, struct snd_seq_queue_owner) -#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER _IOW ('S', 0x44, struct snd_seq_queue_owner) #define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER _IOWR('S', 0x45, struct snd_seq_queue_timer) #define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER _IOW ('S', 0x46, struct snd_seq_queue_timer) -/* XXX -#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct snd_seq_queue_sync) -#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct snd_seq_queue_sync) -*/ #define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT _IOWR('S', 0x49, struct snd_seq_queue_client) #define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT _IOW ('S', 0x4a, struct snd_seq_queue_client) #define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL _IOWR('S', 0x4b, struct snd_seq_client_pool) -- cgit v1.2.3-71-gd317 From 59e4282717cb2adbfd12314ff484669a85827412 Mon Sep 17 00:00:00 2001 From: Thomas Klausner Date: Thu, 3 Mar 2016 16:28:22 +0100 Subject: ALSA: uapi: Make alsa asound.h header more portable This header is used as-is in the alsa-lib userland library, which is portable to other operating systems. For this reason, include linux/types.h only on Linux systems. Add sys/ioctl.h for _IOR/_IOW/etc. (works at least on *BSD and Solaris). Signed-off-by: Thomas Klausner Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index a82108e5d1c0..67bf49d8c944 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -23,7 +23,11 @@ #ifndef _UAPI__SOUND_ASOUND_H #define _UAPI__SOUND_ASOUND_H +#if defined(__KERNEL__) || defined(__linux__) #include +#else +#include +#endif #ifndef __KERNEL__ #include -- cgit v1.2.3-71-gd317 From 67b90cb84be8fde0e51f71834e15c32fbec08562 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:46 +0530 Subject: ALSA: hda - Create common chmap object chmap object represents multichannel capability and contains chmap ops. Legacy driver is updated to use this. With next set of patches chmap object is moved to common to be reused by other drivers (ex: skylake ASoC hdmi driver). Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 41 +++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 81 +++++++++++++++++++++++----------------------- 2 files changed, 81 insertions(+), 41 deletions(-) create mode 100644 include/sound/hda_chmap.h (limited to 'include') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h new file mode 100644 index 000000000000..9ab20f0274a3 --- /dev/null +++ b/include/sound/hda_chmap.h @@ -0,0 +1,41 @@ +/* + * For multichannel support + */ + +#ifndef __SOUND_HDA_CHMAP_H +#define __SOUND_HDA_CHMAP_H + +#include + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; +struct hdac_chmap; + +struct hdac_chmap_ops { + /* + * Helpers for producing the channel map TLVs. These can be overridden + * for devices that have non-standard mapping requirements. + */ + int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels); + void (*cea_alloc_to_tlv_chmap) + (struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels); + + /* check that the user-given chmap is supported */ + int (*chmap_validate)(int ca, int channels, unsigned char *chmap); +}; + +struct hdac_chmap { + unsigned int channels_max; /* max over all cvts */ + struct hdac_chmap_ops ops; + struct hdac_device *hdac; +}; + +#endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index fe4141c43ddb..41f77ce0fa65 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -121,15 +122,6 @@ struct hdmi_ops { int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format); - /* Helpers for producing the channel map TLVs. These can be overridden - * for devices that have non-standard mapping requirements. */ - int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap, - int channels); - void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels); - - /* check that the user-given chmap is supported */ - int (*chmap_validate)(int ca, int channels, unsigned char *chmap); }; struct hdmi_pcm { @@ -155,7 +147,6 @@ struct hdmi_spec { * bit 1 means the second playback PCM, and so on. */ unsigned long pcm_in_use; - unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; struct hdmi_ops ops; @@ -171,6 +162,8 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ struct i915_audio_component_audio_ops i915_audio_ops; bool i915_bound; /* was i915 bound in this driver? */ + + struct hdac_chmap chmap; }; #ifdef CONFIG_SND_HDA_I915 @@ -264,15 +257,6 @@ static int eld_speaker_allocation_bits[] = { [10] = FCH, }; -struct cea_channel_speaker_allocation { - int ca_index; - int speakers[8]; - - /* derived values, just for convenience */ - int channels; - int spk_mask; -}; - /* * ALSA sequence is: * @@ -2141,8 +2125,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) per_cvt->channels_min = 2; if (chans <= 16) { per_cvt->channels_max = chans; - if (chans > spec->channels_max) - spec->channels_max = chans; + if (chans > spec->chmap.channels_max) + spec->chmap.channels_max = chans; } err = snd_hda_query_supported_pcm(codec, cvt_nid, @@ -2368,15 +2352,17 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = spec->channels_max; + uinfo->count = chmap->channels_max; uinfo->value.integer.min = 0; uinfo->value.integer.max = SNDRV_CHMAP_LAST; return 0; } -static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels) { /* If the speaker allocation matches the channel count, it is OK.*/ if (cap->channels != channels) @@ -2409,6 +2395,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; unsigned int __user *dst; int chs, count = 0; @@ -2418,13 +2405,14 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -EFAULT; size -= 8; dst = tlv + 2; - for (chs = 2; chs <= spec->channels_max; chs++) { + for (chs = 2; chs <= chmap->channels_max; chs++) { int i; struct cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; - int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs); + int type = chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, chs); unsigned int tlv_chmap[8]; if (type < 0) @@ -2441,7 +2429,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + chmap->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); if (copy_to_user(dst, tlv_chmap, chs_bytes)) return -EFAULT; dst += chs; @@ -2458,12 +2446,13 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; int pcm_idx = kcontrol->private_value; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); int i; if (!per_pin) { - for (i = 0; i < spec->channels_max; i++) + for (i = 0; i < chmap->channels_max; i++) ucontrol->value.integer.value[i] = 0; return 0; } @@ -2479,6 +2468,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *hchmap = &spec->chmap; int pcm_idx = kcontrol->private_value; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; @@ -2514,8 +2504,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; - if (spec->ops.chmap_validate) { - err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); if (err) return err; } @@ -2806,6 +2796,9 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, .setup_stream = hdmi_setup_stream, +}; + +static const struct hdac_chmap_ops chmap_ops = { .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, }; @@ -2912,6 +2905,8 @@ static int patch_generic_hdmi(struct hda_codec *codec) spec->ops = generic_standard_hdmi_ops; mutex_init(&spec->pcm_lock); + spec->chmap.ops = chmap_ops; + spec->chmap.hdac = &codec->core; codec->spec = spec; hdmi_array_init(spec, 4); @@ -3503,13 +3498,14 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) * - 0x10de0015 * - 0x10de0040 */ -static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, int channels) { if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED; - return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); + return chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, channels); } static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) @@ -3532,9 +3528,9 @@ static int patch_nvhdmi(struct hda_codec *codec) spec = codec->spec; spec->dyn_pin_out = true; - spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; - spec->ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; return 0; } @@ -3893,8 +3889,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; } -static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int atihdmi_paired_chmap_cea_alloc_validate_get_type( + struct hdac_chmap *chmap, + struct cea_channel_speaker_allocation *cap, + int channels) { int c; @@ -4041,10 +4039,11 @@ static int patch_atihdmi(struct hda_codec *codec) if (!has_amd_full_remap_support(codec)) { /* override to ATI/AMD-specific versions with pairwise mapping */ - spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = atihdmi_paired_chmap_cea_alloc_validate_get_type; - spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; - spec->ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.cea_alloc_to_tlv_chmap = + atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; } /* ATI/AMD converters do not advertise all of their capabilities */ @@ -4056,7 +4055,7 @@ static int patch_atihdmi(struct hda_codec *codec) per_cvt->maxbps = max(per_cvt->maxbps, 24u); } - spec->channels_max = max(spec->channels_max, 8u); + spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); return 0; } -- cgit v1.2.3-71-gd317 From 9b3dc8aa3fb1a5f38ca9501f20f6ea4dced10fa0 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:47 +0530 Subject: ALSA: hda - Register chmap obj as priv data instead of codec With this chmap object is added as private data and new ops are added to access driver specific chmap. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 6 +++ sound/pci/hda/patch_hdmi.c | 93 +++++++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 9ab20f0274a3..713b24526134 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -30,6 +30,12 @@ struct hdac_chmap_ops { /* check that the user-given chmap is supported */ int (*chmap_validate)(int ca, int channels, unsigned char *chmap); + + void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap); + void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared); + bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx); }; struct hdac_chmap { diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 41f77ce0fa65..345b83da46c5 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -100,8 +100,6 @@ struct hdmi_spec_per_pin { #endif }; -struct cea_channel_speaker_allocation; - /* operations used by generic code that can be overridden by patches */ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, @@ -2350,9 +2348,7 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; + struct hdac_chmap *chmap = info->private_data; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = chmap->channels_max; @@ -2389,13 +2385,49 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *c WARN_ON(count != channels); } +static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + /* chmap is already set to 0 in caller */ + if (!per_pin) + return; + + memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap)); +} + +static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + mutex_lock(&per_pin->lock); + per_pin->chmap_set = true; + memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); + if (prepared) + hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + mutex_unlock(&per_pin->lock); +} + +static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + return per_pin ? true:false; +} + static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; + struct hdac_chmap *chmap = info->private_data; unsigned int __user *dst; int chs, count = 0; @@ -2444,21 +2476,17 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *chmap = &spec->chmap; + struct hdac_chmap *chmap = info->private_data; int pcm_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + unsigned char pcm_chmap[8]; int i; - if (!per_pin) { - for (i = 0; i < chmap->channels_max; i++) - ucontrol->value.integer.value[i] = 0; - return 0; - } + memset(pcm_chmap, 0, sizeof(pcm_chmap)); + chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = pcm_chmap[i]; - for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) - ucontrol->value.integer.value[i] = per_pin->chmap[i]; return 0; } @@ -2466,20 +2494,17 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - struct hdac_chmap *hchmap = &spec->chmap; + struct hdac_chmap *hchmap = info->private_data; int pcm_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; struct snd_pcm_substream *substream; - unsigned char chmap[8]; + unsigned char chmap[8], per_pin_chmap[8]; int i, err, ca, prepared = 0; /* No monitor is connected in dyn_pcm_assign. * It's invalid to setup the chmap */ - if (!per_pin) + if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) return 0; ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); @@ -2499,7 +2524,9 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, memset(chmap, 0, sizeof(chmap)); for (i = 0; i < ARRAY_SIZE(chmap); i++) chmap[i] = ucontrol->value.integer.value[i]; - if (!memcmp(chmap, per_pin->chmap, sizeof(chmap))) + + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); + if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) return 0; ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) @@ -2509,12 +2536,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, if (err) return err; } - mutex_lock(&per_pin->lock); - per_pin->chmap_set = true; - memcpy(per_pin->chmap, chmap, sizeof(chmap)); - if (prepared) - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); - mutex_unlock(&per_pin->lock); + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); return 0; } @@ -2672,7 +2695,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) if (err < 0) return err; /* override handlers */ - chmap->private_data = codec; + chmap->private_data = &spec->chmap; kctl = chmap->kctl; for (i = 0; i < kctl->count; i++) kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; @@ -2801,9 +2824,11 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { static const struct hdac_chmap_ops chmap_ops = { .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, + .get_chmap = hdmi_get_chmap, + .set_chmap = hdmi_set_chmap, + .is_pcm_attached = is_hdmi_pcm_attached, }; - static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { -- cgit v1.2.3-71-gd317 From f302240da5d5cfc7366dc6994c523aa2aa17c122 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:48 +0530 Subject: ALSA: hda - Use hdac name space for CEA spk alloc structure Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 6 +++--- sound/pci/hda/patch_hdmi.c | 26 ++++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 713b24526134..d3f80e728593 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -7,7 +7,7 @@ #include -struct cea_channel_speaker_allocation { +struct hdac_cea_channel_speaker_allocation { int ca_index; int speakers[8]; @@ -23,9 +23,9 @@ struct hdac_chmap_ops { * for devices that have non-standard mapping requirements. */ int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels); + struct hdac_cea_channel_speaker_allocation *cap, int channels); void (*cea_alloc_to_tlv_chmap) - (struct cea_channel_speaker_allocation *cap, + (struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels); /* check that the user-given chmap is supported */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 345b83da46c5..12b0287784c8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -297,7 +297,7 @@ static int hdmi_channel_mapping[0x32][8] = { * The preceding ones have better chances to be selected by * hdmi_channel_allocation(). */ -static struct cea_channel_speaker_allocation channel_allocations[] = { +static struct hdac_cea_channel_speaker_allocation channel_allocations[] = { /* channel: 7 6 5 4 3 2 1 0 */ { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, /* 2.1 */ @@ -683,7 +683,7 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) static void init_channel_allocations(void) { int i, j; - struct cea_channel_speaker_allocation *p; + struct hdac_cea_channel_speaker_allocation *p; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { p = channel_allocations + i; @@ -791,7 +791,7 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int ca) { struct hdmi_spec *spec = codec->spec; - struct cea_channel_speaker_allocation *ch_alloc; + struct hdac_cea_channel_speaker_allocation *ch_alloc; int i; int err; int order; @@ -2358,7 +2358,7 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, } static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels) + struct hdac_cea_channel_speaker_allocation *cap, int channels) { /* If the speaker allocation matches the channel count, it is OK.*/ if (cap->channels != channels) @@ -2368,8 +2368,9 @@ static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, return SNDRV_CTL_TLVT_CHMAP_VAR; } -static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) +static void hdmi_cea_alloc_to_tlv_chmap( + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) { int count = 0; int c; @@ -2439,7 +2440,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, dst = tlv + 2; for (chs = 2; chs <= chmap->channels_max; chs++) { int i; - struct cea_channel_speaker_allocation *cap; + struct hdac_cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; @@ -3524,7 +3525,7 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) * - 0x10de0040 */ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, int channels) + struct hdac_cea_channel_speaker_allocation *cap, int channels) { if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED; @@ -3805,7 +3806,7 @@ static int atihdmi_paired_swap_fc_lfe(int pos) static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) { - struct cea_channel_speaker_allocation *cap; + struct hdac_cea_channel_speaker_allocation *cap; int i, j; /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ @@ -3916,7 +3917,7 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n static int atihdmi_paired_chmap_cea_alloc_validate_get_type( struct hdac_chmap *chmap, - struct cea_channel_speaker_allocation *cap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) { int c; @@ -3944,8 +3945,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type( return SNDRV_CTL_TLVT_CHMAP_PAIRED; } -static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) +static void atihdmi_paired_cea_alloc_to_tlv_chmap( + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) { /* produce paired maps for pre-rev3 ATI/AMD codecs */ int count = 0; -- cgit v1.2.3-71-gd317 From 739ffee97ed550a2899a925ed3f260fa1e8fa955 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:49 +0530 Subject: ALSA: hda - Add hdmi chmap verb programming ops to chmap object Add slot and channel count programming to hdmi_chmap object and move the chmap_ops to core. Use register_chmap_ops API to register for default ops. Override specific chmap ops in the driver. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 10 ++++++ sound/hda/Makefile | 2 +- sound/hda/hdmi_chmap.c | 49 ++++++++++++++++++++++++++ sound/pci/hda/patch_hdmi.c | 87 +++++++++++++++------------------------------- 4 files changed, 88 insertions(+), 60 deletions(-) create mode 100644 sound/hda/hdmi_chmap.c (limited to 'include') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index d3f80e728593..7afffb90921f 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -36,6 +36,14 @@ struct hdac_chmap_ops { void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap, int prepared); bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx); + + /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ + int (*pin_get_slot_channel)(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot); + int (*pin_set_slot_channel)(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot, int channel); + void (*set_channel_count)(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs); }; struct hdac_chmap { @@ -44,4 +52,6 @@ struct hdac_chmap { struct hdac_device *hdac; }; +void snd_hdac_register_chmap_ops(struct hdac_device *hdac, + struct hdac_chmap *chmap); #endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 7e999c995cdc..3b9bedee2fa4 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o hdac_controller.o hdac_stream.o array.o + hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c new file mode 100644 index 000000000000..6b9879013c86 --- /dev/null +++ b/sound/hda/hdmi_chmap.c @@ -0,0 +1,49 @@ +/* + * HDMI Channel map support helpers + */ + +#include + +static int hdmi_pin_set_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot, int channel) +{ + return snd_hdac_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (channel << 4) | asp_slot); +} + +static int hdmi_pin_get_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot) +{ + return (snd_hdac_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, + asp_slot) & 0xf0) >> 4; +} + +static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid) +{ + return 1 + snd_hdac_codec_read(codec, cvt_nid, 0, + AC_VERB_GET_CVT_CHAN_COUNT, 0); +} + +static void hdmi_set_channel_count(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs) +{ + if (chs != hdmi_get_channel_count(codec, cvt_nid)) + snd_hdac_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); +} + +static const struct hdac_chmap_ops chmap_ops = { + .pin_get_slot_channel = hdmi_pin_get_slot_channel, + .pin_set_slot_channel = hdmi_pin_set_slot_channel, + .set_channel_count = hdmi_set_channel_count, +}; + +void snd_hdac_register_chmap_ops(struct hdac_device *hdac, + struct hdac_chmap *chmap) +{ + chmap->ops = chmap_ops; + chmap->hdac = hdac; +} +EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 12b0287784c8..ac6c702f89db 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -105,12 +105,6 @@ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char *buf, int *eld_size); - /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ - int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot); - int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel); - void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, int ca, int active_channels, int conn_type); @@ -596,20 +590,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); } -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_CVT_CHAN_COUNT, 0); -} - -static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t cvt_nid, int chs) -{ - if (chs != hdmi_get_channel_count(codec, cvt_nid)) - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); -} - /* * ELD proc files */ @@ -778,7 +758,8 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, int channel; for (i = 0; i < 8; i++) { - channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i); + channel = spec->chmap.ops.pin_get_slot_channel( + &codec->core, pin_nid, i); codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", channel, i); } @@ -826,7 +807,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; int hdmi_slot = slotsetup & 0x0f; int channel = (slotsetup & 0xf0) >> 4; - err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel); + err = spec->chmap.ops.pin_set_slot_channel( + &codec->core, pin_nid, hdmi_slot, channel); if (err) { codec_dbg(codec, "HDMI: channel mapping failed\n"); break; @@ -953,8 +935,8 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { int err; - err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, - assignments[hdmi_slot]); + err = spec->chmap.ops.pin_set_slot_channel(&codec->core, + pin_nid, hdmi_slot, assignments[hdmi_slot]); if (err) return -EINVAL; } @@ -990,22 +972,6 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hdmi_debug_channel_mapping(codec, pin_nid); } -static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel) -{ - return snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (channel << 4) | asp_slot); -} - -static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) -{ - return (snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, - asp_slot) & 0xf0) >> 4; -} - /* * Audio InfoFrame routines */ @@ -1181,6 +1147,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, bool non_pcm) { struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; int active_channels; @@ -1207,7 +1174,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, ordered_ca = get_channel_allocation_order(ca); active_channels = channel_allocations[ordered_ca].channels; - hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); + chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, + active_channels); /* * always configure channel mapping, it may have been changed by the @@ -2815,21 +2783,11 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_get_eld = snd_hdmi_get_eld, - .pin_get_slot_channel = hdmi_pin_get_slot_channel, - .pin_set_slot_channel = hdmi_pin_set_slot_channel, .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, .setup_stream = hdmi_setup_stream, }; -static const struct hdac_chmap_ops chmap_ops = { - .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, - .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, - .get_chmap = hdmi_get_chmap, - .set_chmap = hdmi_set_chmap, - .is_pcm_attached = is_hdmi_pcm_attached, -}; - static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { @@ -2931,8 +2889,15 @@ static int patch_generic_hdmi(struct hda_codec *codec) spec->ops = generic_standard_hdmi_ops; mutex_init(&spec->pcm_lock); - spec->chmap.ops = chmap_ops; - spec->chmap.hdac = &codec->core; + snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + hdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap; + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + codec->spec = spec; hdmi_array_init(spec, 4); @@ -3851,9 +3816,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) return 0; } -static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int hdmi_slot, int stream_channel) +static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int hdmi_slot, int stream_channel) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); int verb; int ati_channel_setup = 0; @@ -3886,9 +3852,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); } -static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) +static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int asp_slot) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); bool was_odd = false; int ati_asp_slot = asp_slot; int verb; @@ -4058,8 +4025,6 @@ static int patch_atihdmi(struct hda_codec *codec) spec = codec->spec; spec->ops.pin_get_eld = atihdmi_pin_get_eld; - spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; - spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; spec->ops.setup_stream = atihdmi_setup_stream; @@ -4071,6 +4036,10 @@ static int patch_atihdmi(struct hda_codec *codec) spec->chmap.ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.pin_get_slot_channel = + atihdmi_pin_get_slot_channel; + spec->chmap.ops.pin_set_slot_channel = + atihdmi_pin_set_slot_channel; } /* ATI/AMD converters do not advertise all of their capabilities */ -- cgit v1.2.3-71-gd317 From 828cb4edd8074d35f814e03bee3e2ad8b7d8f64b Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:50 +0530 Subject: ALSA: hda - chmap helper args modified to use generic hdac objs. Chmap helper arguments are modified to use either hdac_device object or hdac_chmap object instead of codec specific object. With this moving these APIs to core will be easier. Helper added to access a specific channel_allocation object instead of directly accessing. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 10 +++-- sound/pci/hda/patch_hdmi.c | 108 ++++++++++++++++++++++++++++----------------- 2 files changed, 74 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 7afffb90921f..f7fd752fc817 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -7,6 +7,9 @@ #include + +#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 + struct hdac_cea_channel_speaker_allocation { int ca_index; int speakers[8]; @@ -24,12 +27,13 @@ struct hdac_chmap_ops { */ int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap, struct hdac_cea_channel_speaker_allocation *cap, int channels); - void (*cea_alloc_to_tlv_chmap) - (struct hdac_cea_channel_speaker_allocation *cap, + void (*cea_alloc_to_tlv_chmap)(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels); /* check that the user-given chmap is supported */ - int (*chmap_validate)(int ca, int channels, unsigned char *chmap); + int (*chmap_validate)(struct hdac_chmap *hchmap, int ca, + int channels, unsigned char *chmap); void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ac6c702f89db..3718b41f4b60 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -696,8 +696,8 @@ static int get_channel_allocation_order(int ca) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_channel_allocation(struct hda_codec *codec, - struct hdmi_eld *eld, int channels) +static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, + int spk_alloc, int channels) { int i; int ca = 0; @@ -717,7 +717,7 @@ static int hdmi_channel_allocation(struct hda_codec *codec, * expand ELD's notions to match the ones used by Audio InfoFrame. */ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (eld->info.spk_alloc & (1 << i)) + if (spk_alloc & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; } @@ -742,36 +742,34 @@ static int hdmi_channel_allocation(struct hda_codec *codec, } } - snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf)); - codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf); return ca; } -static void hdmi_debug_channel_mapping(struct hda_codec *codec, +static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE - struct hdmi_spec *spec = codec->spec; int i; int channel; for (i = 0; i < 8; i++) { - channel = spec->chmap.ops.pin_get_slot_channel( - &codec->core, pin_nid, i); - codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", + channel = chmap->ops.pin_get_slot_channel( + chmap->hdac, pin_nid, i); + dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", channel, i); } #endif } -static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca) { - struct hdmi_spec *spec = codec->spec; struct hdac_cea_channel_speaker_allocation *ch_alloc; int i; int err; @@ -807,10 +805,10 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; int hdmi_slot = slotsetup & 0x0f; int channel = (slotsetup & 0xf0) >> 4; - err = spec->chmap.ops.pin_set_slot_channel( - &codec->core, pin_nid, hdmi_slot, channel); + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, channel); if (err) { - codec_dbg(codec, "HDMI: channel mapping failed\n"); + dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); break; } } @@ -912,12 +910,11 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) } /* set up the channel slots for the given ALSA API channel map */ -static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, +static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, int chs, unsigned char *map, int ca) { - struct hdmi_spec *spec = codec->spec; int ordered_ca = get_channel_allocation_order(ca); int alsa_pos, hdmi_slot; int assignments[8] = {[0 ... 7] = 0xf}; @@ -935,7 +932,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { int err; - err = spec->chmap.ops.pin_set_slot_channel(&codec->core, + err = chmap->ops.pin_set_slot_channel(chmap->hdac, pin_nid, hdmi_slot, assignments[hdmi_slot]); if (err) return -EINVAL; @@ -956,20 +953,20 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) } } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, +static void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca, int channels, unsigned char *map, bool chmap_set) { if (!non_pcm && chmap_set) { - hdmi_manual_setup_channel_mapping(codec, pin_nid, + hdmi_manual_setup_channel_mapping(chmap, pin_nid, channels, map, ca); } else { - hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); + hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); hdmi_setup_fake_chmap(map, ca); } - hdmi_debug_channel_mapping(codec, pin_nid); + hdmi_debug_channel_mapping(chmap, pin_nid); } /* @@ -1142,6 +1139,35 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, } } +static int hdmi_get_active_channels(int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + + return channel_allocations[ordered_ca].channels; +} + +static struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[get_channel_allocation_order(ca)]; +} + +static int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, bool non_pcm, unsigned char *map) +{ + int ca; + + if (!non_pcm && chmap_set) + ca = hdmi_manual_channel_allocation(channels, map); + else + ca = hdmi_channel_allocation_spk_alloc_blk(hdac, + spk_alloc, channels); + + if (ca < 0) + ca = 0; + + return ca; +} + static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) @@ -1152,7 +1178,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int channels = per_pin->channels; int active_channels; struct hdmi_eld *eld; - int ca, ordered_ca; + int ca; if (!channels) return; @@ -1164,15 +1190,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, eld = &per_pin->sink_eld; - if (!non_pcm && per_pin->chmap_set) - ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); - else - ca = hdmi_channel_allocation(codec, eld, channels); - if (ca < 0) - ca = 0; + ca = hdmi_channel_allocation(&codec->core, + eld->info.spk_alloc, channels, + per_pin->chmap_set, non_pcm, per_pin->chmap); - ordered_ca = get_channel_allocation_order(ca); - active_channels = channel_allocations[ordered_ca].channels; + active_channels = hdmi_get_active_channels(ca); chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, active_channels); @@ -1181,9 +1203,9 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, * always configure channel mapping, it may have been changed by the * user in the meantime */ - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap, - per_pin->chmap_set); + hdmi_setup_channel_mapping(&spec->chmap, + pin_nid, non_pcm, ca, channels, + per_pin->chmap, per_pin->chmap_set); spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels, eld->info.conn_type); @@ -2336,7 +2358,7 @@ static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, return SNDRV_CTL_TLVT_CHMAP_VAR; } -static void hdmi_cea_alloc_to_tlv_chmap( +static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels) { @@ -2430,7 +2452,8 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - chmap->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, + tlv_chmap, chs); if (copy_to_user(dst, tlv_chmap, chs_bytes)) return -EFAULT; dst += chs; @@ -2501,7 +2524,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, if (ca < 0) return -EINVAL; if (hchmap->ops.chmap_validate) { - err = hchmap->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + err = hchmap->ops.chmap_validate(hchmap, ca, + ARRAY_SIZE(chmap), chmap); if (err) return err; } @@ -3499,7 +3523,8 @@ static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, chmap, cap, channels); } -static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) +static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) { if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) return -EINVAL; @@ -3769,14 +3794,15 @@ static int atihdmi_paired_swap_fc_lfe(int pos) return pos; } -static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) +static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) { struct hdac_cea_channel_speaker_allocation *cap; int i, j; /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - cap = &channel_allocations[get_channel_allocation_order(ca)]; + cap = hdmi_get_ch_alloc_from_ca(ca); for (i = 0; i < chs; ++i) { int mask = to_spk_mask(map[i]); bool ok = false; @@ -3912,7 +3938,7 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type( return SNDRV_CTL_TLVT_CHMAP_PAIRED; } -static void atihdmi_paired_cea_alloc_to_tlv_chmap( +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, struct hdac_cea_channel_speaker_allocation *cap, unsigned int *chmap, int channels) { -- cgit v1.2.3-71-gd317 From 2f6e8a8518f33b6fac1fb9c79d245e23b6f9b765 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:51 +0530 Subject: ALSA: hda - Move chmap support helpers/ops to core Chmap helpers, ops, controls are moved to core. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 15 + sound/hda/hdmi_chmap.c | 742 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_eld.c | 27 +- sound/pci/hda/patch_hdmi.c | 689 +---------------------------------------- 4 files changed, 759 insertions(+), 714 deletions(-) (limited to 'include') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index f7fd752fc817..5a85b319439a 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -5,6 +5,7 @@ #ifndef __SOUND_HDA_CHMAP_H #define __SOUND_HDA_CHMAP_H +#include #include @@ -58,4 +59,18 @@ struct hdac_chmap { void snd_hdac_register_chmap_ops(struct hdac_device *hdac, struct hdac_chmap *chmap); +int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, + bool non_pcm, unsigned char *map); +int hdmi_get_active_channels(int ca); +void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set); +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); +struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca); +int to_spk_mask(unsigned char c); +int spk_to_chmap(int spk); +int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, + struct hdac_chmap *chmap); #endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index 6b9879013c86..880666aa01c0 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -2,8 +2,182 @@ * HDMI Channel map support helpers */ +#include +#include +#include #include +/* + * CEA speaker placement: + * + * FLH FCH FRH + * FLW FL FLC FC FRC FR FRW + * + * LFE + * TC + * + * RL RLC RC RRC RR + * + * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to + * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. + */ +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ + FLW = (1 << 11), /* Front Left Wide */ + FRW = (1 << 12), /* Front Right Wide */ + FLH = (1 << 13), /* Front Left High */ + FCH = (1 << 14), /* Front Center High */ + FRH = (1 << 15), /* Front Right High */ + TC = (1 << 16), /* Top Center */ +}; + +static const char * const cea_speaker_allocation_names[] = { + /* 0 */ "FL/FR", + /* 1 */ "LFE", + /* 2 */ "FC", + /* 3 */ "RL/RR", + /* 4 */ "RC", + /* 5 */ "FLC/FRC", + /* 6 */ "RLC/RRC", + /* 7 */ "FLW/FRW", + /* 8 */ "FLH/FRH", + /* 9 */ "TC", + /* 10 */ "FCH", +}; + +/* + * ELD SA bits in the CEA Speaker Allocation data block + */ +static int eld_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + /* the following are not defined in ELD yet */ + [7] = FLW | FRW, + [8] = FLH | FRH, + [9] = TC, + [10] = FCH, +}; + +/* + * ALSA sequence is: + * + * surround40 surround41 surround50 surround51 surround71 + * ch0 front left = = = = + * ch1 front right = = = = + * ch2 rear left = = = = + * ch3 rear right = = = = + * ch4 LFE center center center + * ch5 LFE LFE + * ch6 side left + * ch7 side right + * + * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} + */ +static int hdmi_channel_mapping[0x32][8] = { + /* stereo */ + [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* 2.1 */ + [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* Dolby Surround */ + [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* surround40 */ + [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, + /* 4ch */ + [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, + /* surround41 */ + [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, + /* surround50 */ + [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround51 */ + [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, + /* 7.1 */ + [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_channel_allocation(). + */ +static struct hdac_cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, +}; + static int hdmi_pin_set_slot_channel(struct hdac_device *codec, hda_nid_t pin_nid, int asp_slot, int channel) { @@ -34,7 +208,548 @@ static void hdmi_set_channel_count(struct hdac_device *codec, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } +/* + * Channel mapping routines + */ + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct hdac_cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +static int get_channel_allocation_order(int ca) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channel_allocations[i].ca_index == ca) + break; + } + return i; +} + +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) +{ + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { + if (spk_alloc & (1 << i)) + j += snprintf(buf + j, buflen - j, " %s", + cea_speaker_allocation_names[i]); + } + buf[j] = '\0'; /* necessary when j == 0 */ +} +EXPORT_SYMBOL_GPL(snd_print_channel_allocation); + +/* + * The transformation takes two steps: + * + * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => ai->CA + * + * TODO: it could select the wrong CA from multiple candidates. +*/ +static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, + int spk_alloc, int channels) +{ + int i; + int ca = 0; + int spk_mask = 0; + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + /* + * expand ELD's speaker allocation mask + * + * ELD tells the speaker mask in a compact(paired) form, + * expand ELD's notions to match the ones used by Audio InfoFrame. + */ + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (spk_alloc & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ca = channel_allocations[i].ca_index; + break; + } + } + + if (!ca) { + /* + * if there was no match, select the regular ALSA channel + * allocation with the matching number of channels + */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels) { + ca = channel_allocations[i].ca_index; + break; + } + } + } + + snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + ca, channels, buf); + + return ca; +} + +static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int channel; + + for (i = 0; i < 8; i++) { + channel = chmap->ops.pin_get_slot_channel( + chmap->hdac, pin_nid, i); + dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", + channel, i); + } +#endif +} + +static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, + bool non_pcm, + int ca) +{ + struct hdac_cea_channel_speaker_allocation *ch_alloc; + int i; + int err; + int order; + int non_pcm_mapping[8]; + + order = get_channel_allocation_order(ca); + ch_alloc = &channel_allocations[order]; + + if (hdmi_channel_mapping[ca][1] == 0) { + int hdmi_slot = 0; + /* fill actual channel mappings in ALSA channel (i) order */ + for (i = 0; i < ch_alloc->channels; i++) { + while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) + hdmi_slot++; /* skip zero slots */ + + hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; + } + /* fill the rest of the slots with ALSA channel 0xf */ + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) + if (!ch_alloc->speakers[7 - hdmi_slot]) + hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; + } + + if (non_pcm) { + for (i = 0; i < ch_alloc->channels; i++) + non_pcm_mapping[i] = (i << 4) | i; + for (; i < 8; i++) + non_pcm_mapping[i] = (0xf << 4) | i; + } + + for (i = 0; i < 8; i++) { + int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; + int hdmi_slot = slotsetup & 0x0f; + int channel = (slotsetup & 0xf0) >> 4; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, channel); + if (err) { + dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); + break; + } + } +} + +struct channel_map_table { + unsigned char map; /* ALSA API channel map position */ + int spk_mask; /* speaker position bit mask */ +}; + +static struct channel_map_table map_tables[] = { + { SNDRV_CHMAP_FL, FL }, + { SNDRV_CHMAP_FR, FR }, + { SNDRV_CHMAP_RL, RL }, + { SNDRV_CHMAP_RR, RR }, + { SNDRV_CHMAP_LFE, LFE }, + { SNDRV_CHMAP_FC, FC }, + { SNDRV_CHMAP_RLC, RLC }, + { SNDRV_CHMAP_RRC, RRC }, + { SNDRV_CHMAP_RC, RC }, + { SNDRV_CHMAP_FLC, FLC }, + { SNDRV_CHMAP_FRC, FRC }, + { SNDRV_CHMAP_TFL, FLH }, + { SNDRV_CHMAP_TFR, FRH }, + { SNDRV_CHMAP_FLW, FLW }, + { SNDRV_CHMAP_FRW, FRW }, + { SNDRV_CHMAP_TC, TC }, + { SNDRV_CHMAP_TFC, FCH }, + {} /* terminator */ +}; + +/* from ALSA API channel position to speaker bit mask */ +int to_spk_mask(unsigned char c) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->map == c) + return t->spk_mask; + } + return 0; +} +EXPORT_SYMBOL_GPL(to_spk_mask); + +/* from ALSA API channel position to CEA slot */ +static int to_cea_slot(int ordered_ca, unsigned char pos) +{ + int mask = to_spk_mask(pos); + int i; + + if (mask) { + for (i = 0; i < 8; i++) { + if (channel_allocations[ordered_ca].speakers[7 - i] == mask) + return i; + } + } + + return -1; +} + +/* from speaker bit mask to ALSA API channel position */ +int spk_to_chmap(int spk) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} +EXPORT_SYMBOL_GPL(spk_to_chmap); + +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(int ordered_ca, unsigned char slot) +{ + int mask = channel_allocations[ordered_ca].speakers[7 - slot]; + + return spk_to_chmap(mask); +} + +/* get the CA index corresponding to the given ALSA API channel map */ +static int hdmi_manual_channel_allocation(int chs, unsigned char *map) +{ + int i, spks = 0, spk_mask = 0; + + for (i = 0; i < chs; i++) { + int mask = to_spk_mask(map[i]); + + if (mask) { + spk_mask |= mask; + spks++; + } + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if ((chs == channel_allocations[i].channels || + spks == channel_allocations[i].channels) && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) + return channel_allocations[i].ca_index; + } + return -1; +} + +/* set up the channel slots for the given ALSA API channel map */ +static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, + int chs, unsigned char *map, + int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + int alsa_pos, hdmi_slot; + int assignments[8] = {[0 ... 7] = 0xf}; + + for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { + + hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); + + if (hdmi_slot < 0) + continue; /* unassigned channel */ + + assignments[hdmi_slot] = alsa_pos; + } + + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { + int err; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, assignments[hdmi_slot]); + if (err) + return -EINVAL; + } + return 0; +} + +/* store ALSA API channel map from the current default map */ +static void hdmi_setup_fake_chmap(unsigned char *map, int ca) +{ + int i; + int ordered_ca = get_channel_allocation_order(ca); + + for (i = 0; i < 8; i++) { + if (i < channel_allocations[ordered_ca].channels) + map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); + else + map[i] = 0; + } +} + +void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set) +{ + if (!non_pcm && chmap_set) { + hdmi_manual_setup_channel_mapping(chmap, pin_nid, + channels, map, ca); + } else { + hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); + hdmi_setup_fake_chmap(map, ca); + } + + hdmi_debug_channel_mapping(chmap, pin_nid); +} +EXPORT_SYMBOL_GPL(hdmi_setup_channel_mapping); + +int hdmi_get_active_channels(int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + + return channel_allocations[ordered_ca].channels; +} +EXPORT_SYMBOL_GPL(hdmi_get_active_channels); + +struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[get_channel_allocation_order(ca)]; +} +EXPORT_SYMBOL_GPL(hdmi_get_ch_alloc_from_ca); + +int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, bool non_pcm, unsigned char *map) +{ + int ca; + + if (!non_pcm && chmap_set) + ca = hdmi_manual_channel_allocation(channels, map); + else + ca = hdmi_channel_allocation_spk_alloc_blk(hdac, + spk_alloc, channels); + + if (ca < 0) + ca = 0; + + return ca; +} +EXPORT_SYMBOL_GPL(hdmi_channel_allocation); + +/* + * ALSA API channel-map control callbacks + */ +static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chmap->channels_max; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) +{ + /* If the speaker allocation matches the channel count, it is OK.*/ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + + if (!spk) + continue; + + chmap[count++] = spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + unsigned int __user *dst; + int chs, count = 0; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (chs = 2; chs <= chmap->channels_max; chs++) { + int i; + struct hdac_cea_channel_speaker_allocation *cap; + + cap = channel_allocations; + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { + int chs_bytes = chs * 4; + int type = chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, chs); + unsigned int tlv_chmap[8]; + + if (type < 0) + continue; + if (size < 8) + return -ENOMEM; + if (put_user(type, dst) || + put_user(chs_bytes, dst + 1)) + return -EFAULT; + dst += 2; + size -= 8; + count += 8; + if (size < chs_bytes) + return -ENOMEM; + size -= chs_bytes; + count += chs_bytes; + chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, + tlv_chmap, chs); + if (copy_to_user(dst, tlv_chmap, chs_bytes)) + return -EFAULT; + dst += chs; + } + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned char pcm_chmap[8]; + int i; + + memset(pcm_chmap, 0, sizeof(pcm_chmap)); + chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = pcm_chmap[i]; + + return 0; +} + +static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *hchmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned int ctl_idx; + struct snd_pcm_substream *substream; + unsigned char chmap[8], per_pin_chmap[8]; + int i, err, ca, prepared = 0; + + /* No monitor is connected in dyn_pcm_assign. + * It's invalid to setup the chmap + */ + if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) + return 0; + + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + substream = snd_pcm_chmap_substream(info, ctl_idx); + if (!substream || !substream->runtime) + return 0; /* just for avoiding error from alsactl restore */ + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PREPARED: + prepared = 1; + break; + default: + return -EBUSY; + } + memset(chmap, 0, sizeof(chmap)); + for (i = 0; i < ARRAY_SIZE(chmap); i++) + chmap[i] = ucontrol->value.integer.value[i]; + + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); + if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) + return 0; + ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); + if (ca < 0) + return -EINVAL; + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(hchmap, ca, + ARRAY_SIZE(chmap), chmap); + if (err) + return err; + } + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); + + return 0; +} + static const struct hdac_chmap_ops chmap_ops = { + .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, + .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, .pin_get_slot_channel = hdmi_pin_get_slot_channel, .pin_set_slot_channel = hdmi_pin_set_slot_channel, .set_channel_count = hdmi_set_channel_count, @@ -45,5 +760,32 @@ void snd_hdac_register_chmap_ops(struct hdac_device *hdac, { chmap->ops = chmap_ops; chmap->hdac = hdac; + init_channel_allocations(); } EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops); + +int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, + struct hdac_chmap *hchmap) +{ + struct snd_pcm_chmap *chmap; + struct snd_kcontrol *kctl; + int err, i; + + err = snd_pcm_add_chmap_ctls(pcm, + SNDRV_PCM_STREAM_PLAYBACK, + NULL, 0, pcm_idx, &chmap); + if (err < 0) + return err; + /* override handlers */ + chmap->private_data = hchmap; + kctl = chmap->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + kctl->info = hdmi_chmap_ctl_info; + kctl->get = hdmi_chmap_ctl_get; + kctl->put = hdmi_chmap_ctl_put; + kctl->tlv.c = hdmi_chmap_ctl_tlv; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls); diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index bc2e08257c2e..7c6a9737ad8b 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "hda_codec.h" #include "hda_local.h" @@ -42,20 +43,6 @@ enum cea_edid_versions { CEA_EDID_VER_RESERVED = 4, }; -static const char * const cea_speaker_allocation_names[] = { - /* 0 */ "FL/FR", - /* 1 */ "LFE", - /* 2 */ "FC", - /* 3 */ "RL/RR", - /* 4 */ "RC", - /* 5 */ "FLC/FRC", - /* 6 */ "RLC/RRC", - /* 7 */ "FLW/FRW", - /* 8 */ "FLH/FRH", - /* 9 */ "TC", - /* 10 */ "FCH", -}; - static const char * const eld_connection_type_names[4] = { "HDMI", "DisplayPort", @@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec, a->channels, buf, buf2); } -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { - if (spk_alloc & (1 << i)) - j += snprintf(buf + j, buflen - j, " %s", - cea_speaker_allocation_names[i]); - } - buf[j] = '\0'; /* necessary when j == 0 */ -} - void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) { int i; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 3718b41f4b60..bdfa045f28af 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -197,164 +197,6 @@ union audio_infoframe { u8 bytes[0]; }; -/* - * CEA speaker placement: - * - * FLH FCH FRH - * FLW FL FLC FC FRC FR FRW - * - * LFE - * TC - * - * RL RLC RC RRC RR - * - * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to - * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. - */ -enum cea_speaker_placement { - FL = (1 << 0), /* Front Left */ - FC = (1 << 1), /* Front Center */ - FR = (1 << 2), /* Front Right */ - FLC = (1 << 3), /* Front Left Center */ - FRC = (1 << 4), /* Front Right Center */ - RL = (1 << 5), /* Rear Left */ - RC = (1 << 6), /* Rear Center */ - RR = (1 << 7), /* Rear Right */ - RLC = (1 << 8), /* Rear Left Center */ - RRC = (1 << 9), /* Rear Right Center */ - LFE = (1 << 10), /* Low Frequency Effect */ - FLW = (1 << 11), /* Front Left Wide */ - FRW = (1 << 12), /* Front Right Wide */ - FLH = (1 << 13), /* Front Left High */ - FCH = (1 << 14), /* Front Center High */ - FRH = (1 << 15), /* Front Right High */ - TC = (1 << 16), /* Top Center */ -}; - -/* - * ELD SA bits in the CEA Speaker Allocation data block - */ -static int eld_speaker_allocation_bits[] = { - [0] = FL | FR, - [1] = LFE, - [2] = FC, - [3] = RL | RR, - [4] = RC, - [5] = FLC | FRC, - [6] = RLC | RRC, - /* the following are not defined in ELD yet */ - [7] = FLW | FRW, - [8] = FLH | FRH, - [9] = TC, - [10] = FCH, -}; - -/* - * ALSA sequence is: - * - * surround40 surround41 surround50 surround51 surround71 - * ch0 front left = = = = - * ch1 front right = = = = - * ch2 rear left = = = = - * ch3 rear right = = = = - * ch4 LFE center center center - * ch5 LFE LFE - * ch6 side left - * ch7 side right - * - * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} - */ -static int hdmi_channel_mapping[0x32][8] = { - /* stereo */ - [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* 2.1 */ - [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* Dolby Surround */ - [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* surround40 */ - [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, - /* 4ch */ - [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, - /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, - /* surround50 */ - [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround51 */ - [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, - /* 7.1 */ - [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, -}; - -/* - * This is an ordered list! - * - * The preceding ones have better chances to be selected by - * hdmi_channel_allocation(). - */ -static struct hdac_cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 7 6 5 4 3 2 1 0 */ -{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ -{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, - /* Dolby Surround */ -{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, - /* surround40 */ -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, - /* surround41 */ -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, - /* surround50 */ -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* surround51 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - -{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -}; - - /* * HDMI routines */ @@ -653,322 +495,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) } #endif -/* - * Channel mapping routines - */ - -/* - * Compute derived values in channel_allocations[]. - */ -static void init_channel_allocations(void) -{ - int i, j; - struct hdac_cea_channel_speaker_allocation *p; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - p = channel_allocations + i; - p->channels = 0; - p->spk_mask = 0; - for (j = 0; j < ARRAY_SIZE(p->speakers); j++) - if (p->speakers[j]) { - p->channels++; - p->spk_mask |= p->speakers[j]; - } - } -} - -static int get_channel_allocation_order(int ca) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channel_allocations[i].ca_index == ca) - break; - } - return i; -} - -/* - * The transformation takes two steps: - * - * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask - * spk_mask => (channel_allocations[]) => ai->CA - * - * TODO: it could select the wrong CA from multiple candidates. -*/ -static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, - int spk_alloc, int channels) -{ - int i; - int ca = 0; - int spk_mask = 0; - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - - /* - * CA defaults to 0 for basic stereo audio - */ - if (channels <= 2) - return 0; - - /* - * expand ELD's speaker allocation mask - * - * ELD tells the speaker mask in a compact(paired) form, - * expand ELD's notions to match the ones used by Audio InfoFrame. - */ - for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (spk_alloc & (1 << i)) - spk_mask |= eld_speaker_allocation_bits[i]; - } - - /* search for the first working match in the CA table */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) { - ca = channel_allocations[i].ca_index; - break; - } - } - - if (!ca) { - /* if there was no match, select the regular ALSA channel - * allocation with the matching number of channels */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels) { - ca = channel_allocations[i].ca_index; - break; - } - } - } - - snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); - dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ca, channels, buf); - - return ca; -} - -static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int channel; - - for (i = 0; i < 8; i++) { - channel = chmap->ops.pin_get_slot_channel( - chmap->hdac, pin_nid, i); - dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", - channel, i); - } -#endif -} - -static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid, - bool non_pcm, - int ca) -{ - struct hdac_cea_channel_speaker_allocation *ch_alloc; - int i; - int err; - int order; - int non_pcm_mapping[8]; - - order = get_channel_allocation_order(ca); - ch_alloc = &channel_allocations[order]; - - if (hdmi_channel_mapping[ca][1] == 0) { - int hdmi_slot = 0; - /* fill actual channel mappings in ALSA channel (i) order */ - for (i = 0; i < ch_alloc->channels; i++) { - while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) - hdmi_slot++; /* skip zero slots */ - - hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; - } - /* fill the rest of the slots with ALSA channel 0xf */ - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) - if (!ch_alloc->speakers[7 - hdmi_slot]) - hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; - } - - if (non_pcm) { - for (i = 0; i < ch_alloc->channels; i++) - non_pcm_mapping[i] = (i << 4) | i; - for (; i < 8; i++) - non_pcm_mapping[i] = (0xf << 4) | i; - } - - for (i = 0; i < 8; i++) { - int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; - int hdmi_slot = slotsetup & 0x0f; - int channel = (slotsetup & 0xf0) >> 4; - err = chmap->ops.pin_set_slot_channel(chmap->hdac, - pin_nid, hdmi_slot, channel); - if (err) { - dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); - break; - } - } -} - -struct channel_map_table { - unsigned char map; /* ALSA API channel map position */ - int spk_mask; /* speaker position bit mask */ -}; - -static struct channel_map_table map_tables[] = { - { SNDRV_CHMAP_FL, FL }, - { SNDRV_CHMAP_FR, FR }, - { SNDRV_CHMAP_RL, RL }, - { SNDRV_CHMAP_RR, RR }, - { SNDRV_CHMAP_LFE, LFE }, - { SNDRV_CHMAP_FC, FC }, - { SNDRV_CHMAP_RLC, RLC }, - { SNDRV_CHMAP_RRC, RRC }, - { SNDRV_CHMAP_RC, RC }, - { SNDRV_CHMAP_FLC, FLC }, - { SNDRV_CHMAP_FRC, FRC }, - { SNDRV_CHMAP_TFL, FLH }, - { SNDRV_CHMAP_TFR, FRH }, - { SNDRV_CHMAP_FLW, FLW }, - { SNDRV_CHMAP_FRW, FRW }, - { SNDRV_CHMAP_TC, TC }, - { SNDRV_CHMAP_TFC, FCH }, - {} /* terminator */ -}; - -/* from ALSA API channel position to speaker bit mask */ -static int to_spk_mask(unsigned char c) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->map == c) - return t->spk_mask; - } - return 0; -} - -/* from ALSA API channel position to CEA slot */ -static int to_cea_slot(int ordered_ca, unsigned char pos) -{ - int mask = to_spk_mask(pos); - int i; - - if (mask) { - for (i = 0; i < 8; i++) { - if (channel_allocations[ordered_ca].speakers[7 - i] == mask) - return i; - } - } - - return -1; -} - -/* from speaker bit mask to ALSA API channel position */ -static int spk_to_chmap(int spk) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->spk_mask == spk) - return t->map; - } - return 0; -} - -/* from CEA slot to ALSA API channel position */ -static int from_cea_slot(int ordered_ca, unsigned char slot) -{ - int mask = channel_allocations[ordered_ca].speakers[7 - slot]; - - return spk_to_chmap(mask); -} - -/* get the CA index corresponding to the given ALSA API channel map */ -static int hdmi_manual_channel_allocation(int chs, unsigned char *map) -{ - int i, spks = 0, spk_mask = 0; - - for (i = 0; i < chs; i++) { - int mask = to_spk_mask(map[i]); - if (mask) { - spk_mask |= mask; - spks++; - } - } - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if ((chs == channel_allocations[i].channels || - spks == channel_allocations[i].channels) && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) - return channel_allocations[i].ca_index; - } - return -1; -} - -/* set up the channel slots for the given ALSA API channel map */ -static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid, - int chs, unsigned char *map, - int ca) -{ - int ordered_ca = get_channel_allocation_order(ca); - int alsa_pos, hdmi_slot; - int assignments[8] = {[0 ... 7] = 0xf}; - - for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { - - hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); - - if (hdmi_slot < 0) - continue; /* unassigned channel */ - - assignments[hdmi_slot] = alsa_pos; - } - - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { - int err; - - err = chmap->ops.pin_set_slot_channel(chmap->hdac, - pin_nid, hdmi_slot, assignments[hdmi_slot]); - if (err) - return -EINVAL; - } - return 0; -} - -/* store ALSA API channel map from the current default map */ -static void hdmi_setup_fake_chmap(unsigned char *map, int ca) -{ - int i; - int ordered_ca = get_channel_allocation_order(ca); - for (i = 0; i < 8; i++) { - if (i < channel_allocations[ordered_ca].channels) - map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); - else - map[i] = 0; - } -} - -static void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, - hda_nid_t pin_nid, bool non_pcm, int ca, - int channels, unsigned char *map, - bool chmap_set) -{ - if (!non_pcm && chmap_set) { - hdmi_manual_setup_channel_mapping(chmap, pin_nid, - channels, map, ca); - } else { - hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); - hdmi_setup_fake_chmap(map, ca); - } - - hdmi_debug_channel_mapping(chmap, pin_nid); -} - /* * Audio InfoFrame routines */ @@ -1139,35 +665,6 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, } } -static int hdmi_get_active_channels(int ca) -{ - int ordered_ca = get_channel_allocation_order(ca); - - return channel_allocations[ordered_ca].channels; -} - -static struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) -{ - return &channel_allocations[get_channel_allocation_order(ca)]; -} - -static int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, - int channels, bool chmap_set, bool non_pcm, unsigned char *map) -{ - int ca; - - if (!non_pcm && chmap_set) - ca = hdmi_manual_channel_allocation(channels, map); - else - ca = hdmi_channel_allocation_spk_alloc_blk(hdac, - spk_alloc, channels); - - if (ca < 0) - ca = 0; - - return ca; -} - static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) @@ -2331,51 +1828,6 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, }; -/* - * ALSA API channel-map control callbacks - */ -static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *chmap = info->private_data; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = chmap->channels_max; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = SNDRV_CHMAP_LAST; - return 0; -} - -static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, int channels) -{ - /* If the speaker allocation matches the channel count, it is OK.*/ - if (cap->channels != channels) - return -1; - - /* all channels are remappable freely */ - return SNDRV_CTL_TLVT_CHMAP_VAR; -} - -static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, - struct hdac_cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) -{ - int count = 0; - int c; - - for (c = 7; c >= 0; c--) { - int spk = cap->speakers[c]; - if (!spk) - continue; - - chmap[count++] = spk_to_chmap(spk); - } - - WARN_ON(count != channels); -} - static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap) { @@ -2414,127 +1866,6 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) return per_pin ? true:false; } -static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *chmap = info->private_data; - unsigned int __user *dst; - int chs, count = 0; - - if (size < 8) - return -ENOMEM; - if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) - return -EFAULT; - size -= 8; - dst = tlv + 2; - for (chs = 2; chs <= chmap->channels_max; chs++) { - int i; - struct hdac_cea_channel_speaker_allocation *cap; - cap = channel_allocations; - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { - int chs_bytes = chs * 4; - int type = chmap->ops.chmap_cea_alloc_validate_get_type( - chmap, cap, chs); - unsigned int tlv_chmap[8]; - - if (type < 0) - continue; - if (size < 8) - return -ENOMEM; - if (put_user(type, dst) || - put_user(chs_bytes, dst + 1)) - return -EFAULT; - dst += 2; - size -= 8; - count += 8; - if (size < chs_bytes) - return -ENOMEM; - size -= chs_bytes; - count += chs_bytes; - chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, - tlv_chmap, chs); - if (copy_to_user(dst, tlv_chmap, chs_bytes)) - return -EFAULT; - dst += chs; - } - } - if (put_user(count, tlv + 1)) - return -EFAULT; - return 0; -} - -static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *chmap = info->private_data; - int pcm_idx = kcontrol->private_value; - unsigned char pcm_chmap[8]; - int i; - - memset(pcm_chmap, 0, sizeof(pcm_chmap)); - chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); - - for (i = 0; i < sizeof(chmap); i++) - ucontrol->value.integer.value[i] = pcm_chmap[i]; - - return 0; -} - -static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hdac_chmap *hchmap = info->private_data; - int pcm_idx = kcontrol->private_value; - unsigned int ctl_idx; - struct snd_pcm_substream *substream; - unsigned char chmap[8], per_pin_chmap[8]; - int i, err, ca, prepared = 0; - - /* No monitor is connected in dyn_pcm_assign. - * It's invalid to setup the chmap - */ - if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) - return 0; - - ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - substream = snd_pcm_chmap_substream(info, ctl_idx); - if (!substream || !substream->runtime) - return 0; /* just for avoiding error from alsactl restore */ - switch (substream->runtime->status->state) { - case SNDRV_PCM_STATE_OPEN: - case SNDRV_PCM_STATE_SETUP: - break; - case SNDRV_PCM_STATE_PREPARED: - prepared = 1; - break; - default: - return -EBUSY; - } - memset(chmap, 0, sizeof(chmap)); - for (i = 0; i < ARRAY_SIZE(chmap); i++) - chmap[i] = ucontrol->value.integer.value[i]; - - hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); - if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) - return 0; - ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); - if (ca < 0) - return -EINVAL; - if (hchmap->ops.chmap_validate) { - err = hchmap->ops.chmap_validate(hchmap, ca, - ARRAY_SIZE(chmap), chmap); - if (err) - return err; - } - - hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); - - return 0; -} - static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2675,27 +2006,13 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) /* add channel maps */ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { struct hda_pcm *pcm; - struct snd_pcm_chmap *chmap; - struct snd_kcontrol *kctl; - int i; pcm = get_pcm_rec(spec, pcm_idx); if (!pcm || !pcm->pcm) break; - err = snd_pcm_add_chmap_ctls(pcm->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - NULL, 0, pcm_idx, &chmap); + err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap); if (err < 0) return err; - /* override handlers */ - chmap->private_data = &spec->chmap; - kctl = chmap->kctl; - for (i = 0; i < kctl->count; i++) - kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; - kctl->info = hdmi_chmap_ctl_info; - kctl->get = hdmi_chmap_ctl_get; - kctl->put = hdmi_chmap_ctl_put; - kctl->tlv.c = hdmi_chmap_ctl_tlv; } return 0; @@ -2915,9 +2232,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) mutex_init(&spec->pcm_lock); snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - hdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap; spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; @@ -2965,7 +2279,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) generic_hdmi_init_per_pins(codec); - init_channel_allocations(); if (codec_has_acomp(codec)) { codec->depop_delay = 0; -- cgit v1.2.3-71-gd317 From bb63f726f98bec032c7322a9c36eb4167307d856 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 4 Mar 2016 19:59:52 +0530 Subject: ALSA: hda - Use snd_hdac namespace prefix for chmap exported APIs Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hda_chmap.h | 14 +++++++------- sound/hda/hdmi_chmap.c | 38 +++++++++++++++++++------------------- sound/pci/hda/hda_eld.c | 4 ++-- sound/pci/hda/patch_hdmi.c | 14 +++++++------- 4 files changed, 35 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index 5a85b319439a..e20d219a0304 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -59,18 +59,18 @@ struct hdac_chmap { void snd_hdac_register_chmap_ops(struct hdac_device *hdac, struct hdac_chmap *chmap); -int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, +int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc, int channels, bool chmap_set, bool non_pcm, unsigned char *map); -int hdmi_get_active_channels(int ca); -void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, +int snd_hdac_get_active_channels(int ca); +void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca, int channels, unsigned char *map, bool chmap_set); -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); -struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca); -int to_spk_mask(unsigned char c); -int spk_to_chmap(int spk); +void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen); +struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca); +int snd_hdac_chmap_to_spk_mask(unsigned char c); +int snd_hdac_spk_to_chmap(int spk); int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, struct hdac_chmap *chmap); #endif /* __SOUND_HDA_CHMAP_H */ diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index 880666aa01c0..d7ec86263828 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -243,7 +243,7 @@ static int get_channel_allocation_order(int ca) return i; } -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) +void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen) { int i, j; @@ -254,7 +254,7 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) } buf[j] = '\0'; /* necessary when j == 0 */ } -EXPORT_SYMBOL_GPL(snd_print_channel_allocation); +EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation); /* * The transformation takes two steps: @@ -312,7 +312,7 @@ static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, } } - snd_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf)); dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf); @@ -412,7 +412,7 @@ static struct channel_map_table map_tables[] = { }; /* from ALSA API channel position to speaker bit mask */ -int to_spk_mask(unsigned char c) +int snd_hdac_chmap_to_spk_mask(unsigned char c) { struct channel_map_table *t = map_tables; @@ -422,12 +422,12 @@ int to_spk_mask(unsigned char c) } return 0; } -EXPORT_SYMBOL_GPL(to_spk_mask); +EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask); /* from ALSA API channel position to CEA slot */ static int to_cea_slot(int ordered_ca, unsigned char pos) { - int mask = to_spk_mask(pos); + int mask = snd_hdac_chmap_to_spk_mask(pos); int i; if (mask) { @@ -441,7 +441,7 @@ static int to_cea_slot(int ordered_ca, unsigned char pos) } /* from speaker bit mask to ALSA API channel position */ -int spk_to_chmap(int spk) +int snd_hdac_spk_to_chmap(int spk) { struct channel_map_table *t = map_tables; @@ -451,14 +451,14 @@ int spk_to_chmap(int spk) } return 0; } -EXPORT_SYMBOL_GPL(spk_to_chmap); +EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap); /* from CEA slot to ALSA API channel position */ static int from_cea_slot(int ordered_ca, unsigned char slot) { int mask = channel_allocations[ordered_ca].speakers[7 - slot]; - return spk_to_chmap(mask); + return snd_hdac_spk_to_chmap(mask); } /* get the CA index corresponding to the given ALSA API channel map */ @@ -467,7 +467,7 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) int i, spks = 0, spk_mask = 0; for (i = 0; i < chs; i++) { - int mask = to_spk_mask(map[i]); + int mask = snd_hdac_chmap_to_spk_mask(map[i]); if (mask) { spk_mask |= mask; @@ -530,7 +530,7 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca) } } -void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, +void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap, hda_nid_t pin_nid, bool non_pcm, int ca, int channels, unsigned char *map, bool chmap_set) @@ -545,23 +545,23 @@ void hdmi_setup_channel_mapping(struct hdac_chmap *chmap, hdmi_debug_channel_mapping(chmap, pin_nid); } -EXPORT_SYMBOL_GPL(hdmi_setup_channel_mapping); +EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping); -int hdmi_get_active_channels(int ca) +int snd_hdac_get_active_channels(int ca) { int ordered_ca = get_channel_allocation_order(ca); return channel_allocations[ordered_ca].channels; } -EXPORT_SYMBOL_GPL(hdmi_get_active_channels); +EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels); -struct hdac_cea_channel_speaker_allocation *hdmi_get_ch_alloc_from_ca(int ca) +struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca) { return &channel_allocations[get_channel_allocation_order(ca)]; } -EXPORT_SYMBOL_GPL(hdmi_get_ch_alloc_from_ca); +EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca); -int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, +int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc, int channels, bool chmap_set, bool non_pcm, unsigned char *map) { int ca; @@ -577,7 +577,7 @@ int hdmi_channel_allocation(struct hdac_device *hdac, int spk_alloc, return ca; } -EXPORT_SYMBOL_GPL(hdmi_channel_allocation); +EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation); /* * ALSA API channel-map control callbacks @@ -619,7 +619,7 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, if (!spk) continue; - chmap[count++] = spk_to_chmap(spk); + chmap[count++] = snd_hdac_spk_to_chmap(spk); } WARN_ON(count != channels); diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 7c6a9737ad8b..ba7fe9b6655c 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -416,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) if (e->spk_alloc) { char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); codec_dbg(codec, "HDMI: available speakers:%s\n", buf); } @@ -491,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld, snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); - snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bdfa045f28af..eb7da9992aad 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -687,11 +687,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, eld = &per_pin->sink_eld; - ca = hdmi_channel_allocation(&codec->core, + ca = snd_hdac_channel_allocation(&codec->core, eld->info.spk_alloc, channels, per_pin->chmap_set, non_pcm, per_pin->chmap); - active_channels = hdmi_get_active_channels(ca); + active_channels = snd_hdac_get_active_channels(ca); chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, active_channels); @@ -700,7 +700,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, * always configure channel mapping, it may have been changed by the * user in the meantime */ - hdmi_setup_channel_mapping(&spec->chmap, + snd_hdac_setup_channel_mapping(&spec->chmap, pin_nid, non_pcm, ca, channels, per_pin->chmap, per_pin->chmap_set); @@ -3115,9 +3115,9 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - cap = hdmi_get_ch_alloc_from_ca(ca); + cap = snd_hdac_get_ch_alloc_from_ca(ca); for (i = 0; i < chs; ++i) { - int mask = to_spk_mask(map[i]); + int mask = snd_hdac_chmap_to_spk_mask(map[i]); bool ok = false; bool companion_ok = false; @@ -3133,7 +3133,7 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, if (i % 2 == 0 && i + 1 < chs) { /* even channel, check the odd companion */ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); - int comp_mask_req = to_spk_mask(map[i+1]); + int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); int comp_mask_act = cap->speakers[comp_chan_idx]; if (comp_mask_req == comp_mask_act) @@ -3270,7 +3270,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, continue; } - chmap[count++] = spk_to_chmap(spk); + chmap[count++] = snd_hdac_spk_to_chmap(spk); } WARN_ON(count != channels); -- cgit v1.2.3-71-gd317 From fc4f000bf8c0cbf38f44de6bd5e225574e572ed4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Mar 2016 11:34:18 +0100 Subject: ALSA: hda - Fix unexpected resume through regmap code path HD-audio driver has a mechanism to trigger the runtime resume automatically at accessing the verbs. This auto-resume, however, causes the mutex deadlock when invoked from the regmap handler since the regmap keeps the mutex while auto-resuming. For avoiding that, there is some tricky check in the HDA regmap handler to return -EAGAIN error to back-off when the codec is powered down. Then the caller of regmap r/w will retry after properly turning on the codec power. This works in most cases, but there seems a slight race between the codec power check and the actual on-demand auto-resume trigger. This resulted in the lockdep splat, eventually leading to a real deadlock. This patch tries to address the race window by getting the runtime PM refcount at the check time using pm_runtime_get_if_in_use(). With this call, we can keep the power on only when the codec has been already turned on, and back off if not. For keeping the code consistency, the code touching the runtime PM is stored in hdac_device.c although it's used only locally in hdac_regmap.c. Reported-by: Jiri Slaby Cc: Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 2 ++ sound/hda/hdac_device.c | 16 ++++++++++++ sound/hda/hdac_regmap.c | 69 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 64 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index c21c38ce7450..93e63c56f48f 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -168,11 +168,13 @@ int snd_hdac_power_up(struct hdac_device *codec); int snd_hdac_power_down(struct hdac_device *codec); int snd_hdac_power_up_pm(struct hdac_device *codec); int snd_hdac_power_down_pm(struct hdac_device *codec); +int snd_hdac_keep_power_up(struct hdac_device *codec); #else static inline int snd_hdac_power_up(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_down(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_up_pm(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; } +static inline int snd_hdac_keep_power_up(struct hdac_device *codec) { return 0; } #endif /* diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index e361024eabb6..d1a4d6973330 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -611,6 +611,22 @@ int snd_hdac_power_up_pm(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); +/* like snd_hdac_power_up_pm(), but only increment the pm count when + * already powered up. Returns -1 if not powered up, 1 if incremented + * or 0 if unchanged. Only used in hdac_regmap.c + */ +int snd_hdac_keep_power_up(struct hdac_device *codec) +{ + if (!atomic_inc_not_zero(&codec->in_pm)) { + int ret = pm_runtime_get_if_in_use(&codec->dev); + if (!ret) + return -1; + if (ret < 0) + return 0; + } + return 1; +} + /** * snd_hdac_power_down_pm - power down the codec * @codec: the codec object diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index eb8f7c30cb09..bdbcd6b75ff6 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -21,13 +21,16 @@ #include #include -#ifdef CONFIG_PM -#define codec_is_running(codec) \ - (atomic_read(&(codec)->in_pm) || \ - !pm_runtime_suspended(&(codec)->dev)) -#else -#define codec_is_running(codec) true -#endif +static int codec_pm_lock(struct hdac_device *codec) +{ + return snd_hdac_keep_power_up(codec); +} + +static void codec_pm_unlock(struct hdac_device *codec, int lock) +{ + if (lock == 1) + snd_hdac_power_down_pm(codec); +} #define get_verb(reg) (((reg) >> 8) & 0xfff) @@ -238,20 +241,28 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) struct hdac_device *codec = context; int verb = get_verb(reg); int err; + int pm_lock = 0; - if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE) - return -EAGAIN; + if (verb != AC_VERB_GET_POWER_STATE) { + pm_lock = codec_pm_lock(codec); + if (pm_lock < 0) + return -EAGAIN; + } reg |= (codec->addr << 28); - if (is_stereo_amp_verb(reg)) - return hda_reg_read_stereo_amp(codec, reg, val); - if (verb == AC_VERB_GET_PROC_COEF) - return hda_reg_read_coef(codec, reg, val); + if (is_stereo_amp_verb(reg)) { + err = hda_reg_read_stereo_amp(codec, reg, val); + goto out; + } + if (verb == AC_VERB_GET_PROC_COEF) { + err = hda_reg_read_coef(codec, reg, val); + goto out; + } if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE) reg &= ~AC_AMP_FAKE_MUTE; err = snd_hdac_exec_verb(codec, reg, 0, val); if (err < 0) - return err; + goto out; /* special handling for asymmetric reads */ if (verb == AC_VERB_GET_POWER_STATE) { if (*val & AC_PWRST_ERROR) @@ -259,7 +270,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) else /* take only the actual state */ *val = (*val >> 4) & 0x0f; } - return 0; + out: + codec_pm_unlock(codec, pm_lock); + return err; } static int hda_reg_write(void *context, unsigned int reg, unsigned int val) @@ -267,6 +280,7 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) struct hdac_device *codec = context; unsigned int verb; int i, bytes, err; + int pm_lock = 0; if (codec->caps_overwriting) return 0; @@ -275,14 +289,21 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) reg |= (codec->addr << 28); verb = get_verb(reg); - if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE) - return codec->lazy_cache ? 0 : -EAGAIN; + if (verb != AC_VERB_SET_POWER_STATE) { + pm_lock = codec_pm_lock(codec); + if (pm_lock < 0) + return codec->lazy_cache ? 0 : -EAGAIN; + } - if (is_stereo_amp_verb(reg)) - return hda_reg_write_stereo_amp(codec, reg, val); + if (is_stereo_amp_verb(reg)) { + err = hda_reg_write_stereo_amp(codec, reg, val); + goto out; + } - if (verb == AC_VERB_SET_PROC_COEF) - return hda_reg_write_coef(codec, reg, val); + if (verb == AC_VERB_SET_PROC_COEF) { + err = hda_reg_write_coef(codec, reg, val); + goto out; + } switch (verb & 0xf00) { case AC_VERB_SET_AMP_GAIN_MUTE: @@ -319,10 +340,12 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff); err = snd_hdac_exec_verb(codec, reg, 0, NULL); if (err < 0) - return err; + goto out; } - return 0; + out: + codec_pm_unlock(codec, pm_lock); + return err; } static const struct regmap_config hda_regmap_cfg = { -- cgit v1.2.3-71-gd317 From a1ce94d03bfdfaa1ef31c1a84383105888ac5f3b Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Wed, 2 Mar 2016 19:26:28 +0100 Subject: ALSA: seq: Provide card number / PID via sequencer client info rawmidi devices expose the card number via IOCTLs, which allows to find the corresponding device in sysfs. The sequencer provides no identifing data. Chromium works around this issue by scanning rawmidi as well as sequencer devices and matching them by using assumtions, how the kernel register sequencer devices. This changes adds support for exposing the card number for kernel clients as well as the PID for user client. The minor of the API version is changed to distinguish between the zero initialised reserved field and card number 0. [minor coding style fixes by tiwai] Signed-off-by: Martin Koegler Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- include/uapi/sound/asequencer.h | 6 ++++-- sound/core/seq/seq_clientmgr.c | 14 ++++++++++++++ sound/core/seq/seq_clientmgr.h | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index af96f2044f91..7b7659a79ac4 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -25,7 +25,7 @@ #include /** version of the sequencer */ -#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1) +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2) /** * definition of sequencer event types @@ -357,7 +357,9 @@ struct snd_seq_client_info { unsigned char event_filter[32]; /* event filter bitmap */ int num_ports; /* RO: number of ports */ int event_lost; /* number of lost events */ - char reserved[64]; /* for future use */ + int card; /* RO: card number[kernel] */ + int pid; /* RO: pid[user] */ + char reserved[56]; /* for future use */ }; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 58e79e02f217..d6d9419d8bac 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -364,6 +364,7 @@ static int snd_seq_open(struct inode *inode, struct file *file) /* fill client data */ user->file = file; sprintf(client->name, "Client-%d", c); + client->data.user.owner = get_pid(task_pid(current)); /* make others aware this new client */ snd_seq_system_client_ev_client_start(c); @@ -380,6 +381,7 @@ static int snd_seq_release(struct inode *inode, struct file *file) seq_free_client(client); if (client->data.user.fifo) snd_seq_fifo_delete(&client->data.user.fifo); + put_pid(client->data.user.owner); kfree(client); } @@ -1197,6 +1199,17 @@ static void get_client_info(struct snd_seq_client *cptr, info->event_lost = cptr->event_lost; memcpy(info->event_filter, cptr->event_filter, 32); info->num_ports = cptr->num_ports; + + if (cptr->type == USER_CLIENT) + info->pid = pid_vnr(cptr->data.user.owner); + else + info->pid = -1; + + if (cptr->type == KERNEL_CLIENT) + info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1; + else + info->card = -1; + memset(info->reserved, 0, sizeof(info->reserved)); } @@ -2271,6 +2284,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index, client->accept_input = 1; client->accept_output = 1; + client->data.kernel.card = card; va_start(args, name_fmt); vsnprintf(client->name, sizeof(client->name), name_fmt, args); diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index 20f0a725ec7d..c6614254ef8a 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -33,6 +33,7 @@ struct snd_seq_user_client { struct file *file; /* file struct of client */ /* ... */ + struct pid *owner; /* fifo */ struct snd_seq_fifo *fifo; /* queue for incoming events */ @@ -41,6 +42,7 @@ struct snd_seq_user_client { struct snd_seq_kernel_client { /* ... */ + struct snd_card *card; }; -- cgit v1.2.3-71-gd317